2017-10-06 2 views
2

大きなXMLファイルを処理していて、アプリケーションの実行中にXmlTextReader.ReadOuterXml()メソッドがメモリ例外をスローしています。ReadOuterXmlが大きな(1 GB)XMLファイルの一部を読み出すOutOfMemoryExceptionをスローしています

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

どのサイズ我々はここで話しているの? – Flater

+0

読み込まれたxmlのサイズが大きすぎます。イテレータを使ってメモリを節約する 'GetDetails'の結果を得ることができます。 – Fabio

+0

こんにちは@Flater、XMLのサイズはほぼ1 GBです – Aniket

答えて

0

最も可能性が高い理由は、あなたが文字列に1ギガバイトのXML文書のかなりの部分を読み込むしようとしている、とMaximum string length in .Netをヒットしているということです。

だから、しないでください。が代わりにXmlReader.ReadSubtree()XDocument.Load()を使用してXmlReaderから直接ロード:GetDetails()

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; 
     } 
    } 
} 

そして操作を行います。

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にコピーします(次に2回目の解析を行います)。このバージョンのアルゴリズムは、この余分な作業を完全にスキップします。 large object heapを実行するのに十分な大きさの文字列を作成することも回避され、パフォーマンスの問題がさらに発生する可能性があります。

これはまだあなたが一度に一つの要素<B>をロードする場所をSAX-likeがあなたの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(); 
      } 
     } 
    } 
} 

:まず、次の拡張方法を紹介するXMLファイルの

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

関連する問題