XmlReader 跳过相邻元素

Posted

技术标签:

【中文标题】XmlReader 跳过相邻元素【英文标题】:XmlReader skipping adjoined elements 【发布时间】:2017-07-08 12:22:01 【问题描述】:

在尝试最小化 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.NodeTypeText,而不是 Element。第二个测试(&lt;/entry&gt;&lt;entry&gt; 之间的空格通过处理所有 4 个。

在我的真实示例中,我不能轻易更改源。我已经有了一个受another *** 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】:

当您调用 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();
    

【讨论】:

啊,明白了。所以用while(!xml.EOF)做一些与你类似的事情,在if条件内我会调用continue,否则(不需要else)我会做xml.Read()

以上是关于XmlReader 跳过相邻元素的主要内容,如果未能解决你的问题,请参考以下文章

XMLReader 正在跳过元素

为啥 XmlReader 跳过标签?

XmlReader - 自关闭元素不会触发 EndElement 事件?

从 XmlReader 打印出元素值

C# 使用 XMLReader(?) 读取子元素

使用 xmlReader 在 C# 中过滤特定元素值的大型 XML