虽然尝试最小化XML解析程序的内存占用,特别是避免使用XElement.Load()
加载数百兆字节,但我发现文章建议使用较旧的XmlReader
e.g. here。
我需要在内部将每个主要元素重建为XElement
以避免重大重构。但是,我发现如果我的源元素直接相邻,这种方法会跳过每个第二个元素。
我已将问题拆解到此单元测试(MSTest2 with FluentAssertions):
[DataTestMethod]
[DataRow("<data><entry>1</entry><entry>2</entry><entry>3</entry><entry>4</entry></data>")]
[DataRow("<data><entry>1</entry> <entry>2</entry> <entry>3</entry> <entry>4</entry></data>")]
public void XmlReaderCount(string input)
{
var sr = new StringReader(input);
var xml = XmlReader.Create(sr);
xml.MoveToContent();
var data = new List<string>();
while (xml.Read())
{
if (xml.LocalName == "entry" && xml.NodeType == XmlNodeType.Element)
{
var element = (XElement)System.Xml.Linq.XNode.ReadFrom(xml);
data.Add(element.Value);
}
}
data.Should()
.HaveCount(4);
}
第一次(数据驱动)测试失败:
预期收集包含4个项目,但找到2个。
因为它将1和3放入数据集合中。它确实循环了4次,但每个其他元素都有xml.NodeType
Text
,而不是Element
。第二个测试(</entry>
和<entry>
之间的空格通过处理所有4。
在我的真实世界的例子中,我无法轻易改变来源。我已经有了一个灵感来自another StackOverflow question的解决方案,所以我可以做到以下几点,但这看起来很奇怪 - 有什么不对吗?
[DataTestMethod]
[DataRow("<data><entry>1</entry><entry>2</entry><entry>3</entry><entry>4</entry></data>")]
[DataRow("<data><entry>1</entry> <entry>2</entry> <entry>3</entry> <entry>4</entry></data>")]
public void XmlReaderCountSubtree(string input)
{
var data = new List<string>();
var sr = new StringReader(input);
var xml = XmlReader.Create(sr);
xml.MoveToContent();
while (xml.Read())
{
if (xml.LocalName == "entry" && xml.NodeType == XmlNodeType.Element)
{
using (var subtree = xml.ReadSubtree())
{
subtree.MoveToContent();
var content = subtree.ReadOuterXml();
var element = XElement.Parse(content);
data.Add(element.Value);
}
}
}
data.Should()
.HaveCount(4);
}
答案 0 :(得分:1)
当您致电ReadFrom(xml)
时,xml的状态会发生变化。它的光标向前移动到下一个元素。然后,您的代码将移至while (xml.Read())
,因此完全忽略该新元素。
使用第二个数据集时,忽略的(和未检查的)元素是空白节点,因此您可以使用它。但基本上,你阅读算法是错误的。
修复你的第一种方法,不是很漂亮,但它有效:
xml.Read();
while (! xml.EOF)
{
if (xml.LocalName == "entry" && xml.NodeType == XmlNodeType.Element)
{
//using (var subtree = xml.ReadSubtree())
{
var element = (XElement)XNode.ReadFrom(xml);
data.Add(element.Value);
}
}
else
{
xml.Read();
}
}