我正在处理一个很大的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()
行抛出异常。如何避免这种情况呢?的大小几乎是1 GB。
发布于 2017-10-08 15:08:20
在ReadOuterXml()
中获得OutOfMemoryException
的最有可能的原因是,您试图将1 GB的XML文档中的很大一部分读入到字符串中,并且命中Maximum string length in .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
(然后对其进行第二次解析)。这个版本的算法完全跳过了这个额外的工作。它还避免了创建足够大的字符串以在large object heap上运行,这可能会导致额外的性能问题。
如果这仍然使用了太多的内存,那么您将需要为您的实现SAX-like解析,一次只加载一个元素<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.
}
}
发布于 2021-03-11 23:21:26
using (var reader = XmlReader.Create(fileName))
{
XmlDocument oXml = new XmlDocument();
while (reader.Read())
{
oXml.Load(reader);
}
}
对于我来说,上面的代码解决了我们通过XmlDocument Load方法将其返回给XmlDocument时的问题
https://stackoverflow.com/questions/46603999
复制相似问题