使用XDocument循环遍历大型XML文件

3
我需要从一个现有的XML文件中复制节点到一个新创建的XML文件中。我正在使用XDocument实例来访问现有的XML文件。问题是XML文件可能非常大(比如说Openstreetmap数据,500K行)。如何循环遍历大型XML文件而不会导致内存错误呢?目前,我只是使用XDocument.Load(path)并循环遍历doc.Descendants(),但这会导致程序在循环完成之前冻结。所以我认为我必须异步循环,但我不知道实现这一点的最佳方法。

2
XmlReaderIEnumerable 迭代器 (yield) - Fabio
1个回答

7
你可以使用XmlReaderIEnumerable<XElement>迭代器来产生所需的元素。这种方法虽然不是异步的,但它可以节省内存,因为你不需要将整个文件加载到内存中进行处理,只需选择要复制的元素即可。
public IEnumerable<XElement> ReadFile(string pathToTheFile)
{
    using (XmlReader reader = XmlReader.Create(pathToTheFile))
    {
        reader.MoveToContent();
        while (reader.Read())
        {
            If (reader.NodeType == XmlNodeType.Element)
            {
                if (reader.Name.Equals("yourElementName"))
                {
                    XElement element = XElement.ReadFrom(reader) as XElement;
                    yield return element ;
                }
            }
        }
    }
}

您可以异步读取文件

public async Task<IEnumerable<XElement>> ReadFileAsync(string pathToTheFile)
{
    var elements = new List<XElement>();
    var xmlSettings = new XmlReaderSettings { Async = true };
    using (XmlReader reader = XmlReader.Create(pathToTheFile, xmlSettings))
    {
        await reader.MoveToContentAsync();
        while (await reader.ReadAsync())
        {
            If (reader.NodeType == XmlNodeType.Element)
            {
                if (reader.Name.Equals("yourElementName"))
                {
                    XElement element = XElement.ReadFrom(reader) as XElement;
                    elements.Add(element);
                }
            }
        }
    }

    return elements;
}

然后您可以异步循环所有文件并等待结果。

var fileTask1 = ReadFileAsync(filePath1);
var fileTask2 = ReadFileAsync(filePath2);
var fileTask3 = ReadFileAsync(filePath3);

await Task.WhenAll(new Task[] { fileTask1, fileTask2, fileTask3} );

// use results
var elementsFromFile1 = fileTask1.Result;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接