XmlReader跳过相邻的元素

时间:2017-07-08 12:22:02

标签: c# xml-parsing

虽然尝试最小化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);
}

1 个答案:

答案 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();
    }
}