![](/img/trans.png)
[英]OutofMemoryException while reading large XML file in C# windows forms
[英]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.