为啥 XmlReader 跳过标签?

Posted

技术标签:

【中文标题】为啥 XmlReader 跳过标签?【英文标题】:Why is XmlReader skipping tags?为什么 XmlReader 跳过标签? 【发布时间】:2019-01-19 10:44:18 【问题描述】:

XmlReader 正在跳过标签,我不知道为什么。如果你执行下面的代码,你会看到 ID 123 和 789 被打印出来,这意味着 456 被跳过了。如果您运行使用 1234 的替代内存流,您将看到仅打印 123 和 456 意味着 1234 和 789 被跳过。

这是主要部分。完整代码如下。我创建了一个带有流的 XML 阅读器,我有一个 while 循环,我检查我想要的深度(因为节点不在深度 0 处)。 XElement.ReadFrom(root) as XElement 根据我的理解读取 XML 直到标签关闭,这就是我想要的。然后我简单地打印出 id 值。很简单,不到10行代码

var root = XmlReader.Create(fs);
root.MoveToElement();
while (root.Read())

    if (root.NodeType == XmlNodeType.EndElement || root.Depth != 1)
        continue;
    var el = XElement.ReadFrom(root) as XElement;
    Console.WriteLine(el.Attribute("id").Value);

如果我在</node><node> 之间添加换行符,它不会跳过标签,但感觉就像是创可贴,我不确定我的输入是否会在</node> 之后有换行符。


using System.Xml.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace nearMe

    class Program
    
        static void Main(string[] args)
        
            using (var fs = new MemoryStream(UTF8Encoding.UTF8.GetBytes(@"<?xml version=""1.0"" encoding=""utf-8""?><test><node id=""123"">
        <tag k=""amenity"" v=""fuel"" />
    </node><node id=""456"">
        <tag k=""name"" v=""B"" />
    </node><node id=""789"">
        <tag k=""amenity"" v=""test"" />
    </node></test>")))
            
                var root = XmlReader.Create(fs);
                root.MoveToElement();

                while (root.Read())
                
                    if (root.NodeType == XmlNodeType.EndElement || root.Depth != 1)
                        continue;
                    var el = XElement.ReadFrom(root) as XElement;
                    if (el == null)
                        continue;
                    if (el.Name != "node")
                        continue;

                    Console.WriteLine(el.Attribute("id").Value);
                
            
        
    


using (var fs = new MemoryStream(UTF8Encoding.UTF8.GetBytes(@"<?xml version=""1.0"" encoding=""utf-8""?>
    <test>
        <node id=""123"">
            <tag k=""amenity"" v=""fuel"" />
        </node>
        <node id=""1234"">
            <tag k=""amenity"" v=""fuel"" />
        </node>
        <node id=""456"">
            <tag k=""name"" v=""B"" />
        </node>
        <node id=""789"">
            <tag k=""amenity"" v=""test"" />
        </node>
    </test>"
)))

【问题讨论】:

您可能已经注意到,每隔一个元素就会被跳过。除此之外,如果您知道节点名称,我建议您按名称读取节点。恕我直言,阅读更容易。 您没有获得所有元素的原因是由于获得了第一个节点,然后您位于第二个节点,然后跳过第二个节点移动到下一个节点。我的解决方案检查你是否在一个节点上,所以你不要移动。 【参考方案1】:

在类似问题的评论中指出了您的错误:

调用 [XElement.ReadFrom] 读取元素并转到下一个元素,然后以下 [root.Read()] 再次读取下一个元素。如果它们碰巧具有相同的名称并且是连续的,那么您基本上会错过一个元素。 (pbz)

解决此问题的最简单方法是删除 ReadFrom 并继续从阅读器获取值:

            while (root.Read())
            
                if (root.NodeType != XmlNodeType.Element || 
                    root.Depth != 1 ||
                    root.Name != "node")
                    continue;

                Console.WriteLine(root.GetAttribute("id"));
            

【讨论】:

【参考方案2】:
using (var fs = new FileStream("test.xml", FileMode.Open, FileAccess.Read))

    var test = XElement.Load(fs);
    var nodes = test.XPathSelectElements("node[@id]");

    foreach (var node in nodes)
    
        Console.WriteLine(node.Attribute("id").Value);
    

【讨论】:

【参考方案3】:

使用下面的代码。它不会跳过并使用 xml 阅读器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace ConsoleApplication1

    class Program
    
        static void Main(string[] args)
        

             using (var fs = new MemoryStream(UTF8Encoding.UTF8.GetBytes(@"<?xml version=""1.0"" encoding=""utf-8""?><test><node id=""123"">
                            <tag k=""amenity"" v=""fuel"" />
                        </node><node id=""456"">
                            <tag k=""name"" v=""B"" />
                        </node><node id=""789"">
                            <tag k=""amenity"" v=""test"" />
                        </node></test>")))
            
                XmlReader reader = XmlReader.Create(fs);

                while (!reader.EOF)
                
                    if(reader.Name != "node")
                    
                        reader.ReadToFollowing("node");
                    
                    if(!reader.EOF)
                    
                        XElement node = (XElement)XElement.ReadFrom(reader);
                        int id = (int)node.Attribute("id");
                        Console.WriteLine(id.ToString());
                    
                
            
            Console.ReadLine();

        
    

【讨论】:

以上是关于为啥 XmlReader 跳过标签?的主要内容,如果未能解决你的问题,请参考以下文章

XmlReader 跳过相邻元素

XMLReader 正在跳过元素

为啥 XmlReader 中的默认编码与 XmlTextReader 默认编码的行为不同?

使用 PHP XMLReader 检测 XML 自闭标签

xml 文档异常中禁止的 DTD

使用XmlDocument时跳过DTD验证