簡體   English   中英

ReadOuterXml 正在拋出 OutOfMemoryException 讀取大(1 GB)XML 文件的一部分

[英]ReadOuterXml is throwing OutOfMemoryException reading part of large (1 GB) XML file

我正在處理一個大型 XML 文件,在運行應用程序時, XmlTextReader.ReadOuterXml()方法拋出內存異常。

代碼行就像,

XmlTextReader xr = null;
try
{
    xr = new XmlTextReader(fileName);
    while (xr.Read() && success)
    {
        if (xr.NodeType != XmlNodeType.Element) 
            continue;
        switch (xr.Name)
        {
            case "A":
                var xml = xr.ReadOuterXml();
                var n = GetDetails(xml);
                break;
        }
    }
}
catch (Exception ex)
{
    //Do stuff
}

使用:

private int GetDetails (string xml)
{

    var rootNode = XDocument.Parse(xml);
    var xnodes = rootNode.XPathSelectElements("//A/B").ToList();
    //Then  working on list of nodes

}

現在,在加載 XML 文件時,應用程序在xr.ReadOuterXml()行上拋出異常。 可以做些什么來避免這種情況? XML 的大小接近 1 GB。

您在ReadOuterXml()中收到OutOfMemoryException的最可能原因是您試圖將 1 GB XML 文檔的大部分讀入一個字符串,並且達到了.Net 中最大字符串長度

所以,不要那樣做。 而是使用XDocument.Load()XmlReader.ReadSubtree()直接從XmlReader加載:

using (var xr = XmlReader.Create(fileName))
{
    while (xr.Read() && success)
    {
        if (xr.NodeType != XmlNodeType.Element)
            continue;
        switch (xr.Name)
        {
            case "A":
                {
                    // ReadSubtree() positions the reader at the EndElement of the element read, so the 
                    // next call to Read() moves to the next node.
                    using (var subReader = xr.ReadSubtree())
                    {
                        var doc = XDocument.Load(subReader);
                        GetDetails(doc);
                    }
                }
                break;
        }
    }
}

然后在GetDetails()執行:

private int GetDetails(XDocument rootDocument)
{
    var xnodes = rootDocument.XPathSelectElements("//A/B").ToList();
    //Then  working on list of nodes
    return xnodes.Count;
}

這不僅會使用更少的內存,而且性能也會更高。 ReadOuterXml()使用臨時XmlWriter將輸入流中的 XML 復制到輸出StringWriter (然后您再次解析)。 這個版本的算法完全跳過了這個額外的工作。 它還避免創建足夠大的字符串以進入大對象堆,這可能會導致額外的性能問題。

如果這仍然使用太多內存,您將需要為您的 XML 實現類似 SAX 的解析,其中您一次只加載一個元素<B> 首先介紹如下擴展方法:

public static partial class XmlReaderExtensions
{
    public static IEnumerable<XElement> WalkXmlElements(this XmlReader xmlReader, Predicate<Stack<XName>> filter)
    {
        Stack<XName> names = new Stack<XName>();

        while (xmlReader.Read())
        {
            if (xmlReader.NodeType == XmlNodeType.Element)
            {
                names.Push(XName.Get(xmlReader.LocalName, xmlReader.NamespaceURI));
                if (filter(names))
                {
                    using (var subReader = xmlReader.ReadSubtree())
                    {
                        yield return XElement.Load(subReader);
                    }
                }
            }

            if ((xmlReader.NodeType == XmlNodeType.Element && xmlReader.IsEmptyElement)
                || xmlReader.NodeType == XmlNodeType.EndElement)
            {
                names.Pop();
            }
        }
    }
}

然后,按如下方式使用它:

using (var xr = XmlReader.Create(fileName))
{
    Predicate<Stack<XName>> filter =
        (stack) => stack.Peek().LocalName == "B" && stack.Count > 1 && stack.ElementAt(1).LocalName == "A";
    foreach (var element in xr.WalkXmlElements(filter))
    {
        //Then working on the specific node.
    }
}
using (var reader = XmlReader.Create(fileName))
{   
    XmlDocument oXml = new XmlDocument();
    while (reader.Read())
        {                  
            oXml.Load(reader);                    
        }
}

對我來說,當我們通過 XmlDocument Load 方法將其返回到 XmlDocument 時,上面的代碼解決了問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM