2017-10-06 97 views
2

我正在處理大型XML文件,並且在運行應用程序時,XmlTextReader.ReadOuterXml()方法正在拋出內存異常。代碼ReadOuterXml拋出OutOfMemoryException讀取大型(1 GB)XML文件的一部分

線都喜歡,使用

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。

+1

我們在談論什麼尺寸的XML文件的有關在這裏:

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.Name, 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(); } } } } 

然後,按如下方式使用它? – Flater

+0

簡單的加載xml的大小太大了。你可以考慮使用迭代器和GetDetails的結果來保持內存低。 – Fabio

+0

Hi @Flater,XML的大小差不多是1 GB – Aniket

回答

0

最可能的原因,你得到一個在ReadOuterXml()OutOfMemoryException是您試圖在1 GB XML文檔轉換成字符串的很大一部分來讀取,並擊中Maximum string length in .Net

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

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(然後再解析該文件)。該版本的算法完全跳過了這項額外的工作。它還避免了創建足夠大的字符串以進入large object heap,這可能會導致其他性能問題。

如果仍然使用太多內存,你將需要實現SAX-like解析爲XML,你一次只能裝載一個元素<B>。首先,介紹以下擴展方法:

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. 
    } 
} 
+0

謝謝@dbc ...這真的有幫助。 – Aniket