【问题标题】:Why is the XmlReader skipping elements?为什么 XmlReader 跳过元素?
【发布时间】:2016-08-22 22:18:52
【问题描述】:

请注意,这个问题是针对XmlReader 而不是使用XDocument 或XmlReader。

我有一个 XML 片段:

private string GetXmlFragment()
{
    return @"<bookstore>
          <book genre='novel' ISBN='10-861003-324'>
            <title>The Handmaid's Tale</title>
            <price>19.95</price>
          </book>
          <book genre='novel' ISBN='1-861001-57-5'>
            <title>Pride And Prejudice</title>
            <price>24.95</price>
          </book>
        </bookstore>";
}

我还有一个扩展方法为:

public static IEnumerable<XElement> GetElement(this XmlReader reader, string elementName)
{
    reader.MoveToElement();

    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element 
            && reader.Name.Equals(elementName, StringComparison.InvariantCulture))
        {
            yield return XNode.ReadFrom(reader) as XElement;
        }
    }
}

然后我尝试通过以下方式获取两个 book 元素:

var xmlReaderSettings = new XmlReaderSettings
{
    CheckCharacters = false,
    ConformanceLevel = ConformanceLevel.Fragment,
    IgnoreComments = true,
    IgnoreWhitespace = true,
    IgnoreProcessingInstructions = true
};

using (var stringReader = new StringReader(this.GetXmlFragment()))
using (var xmlReader = XmlReader.Create(stringReader, xmlReaderSettings))
{
    xmlReader.GetElement("book").Count().ShouldBe(2);
}

但是我只得到第一个元素,调试显示,一旦我得到第一个元素,阅读器就会跳转到第二个book 元素的title。

解决方案灵感来自HERE

非常感谢任何帮助。

【问题讨论】:

  • 无法重现。您的方法在我的机器上生成一个包含两个元素的序列。您确定 this.GetXmlFragment() 生成您提供的 XML 吗?
  • 奇怪,我已经更新了问题以包含方法
  • 见dotnetfiddle.net/ssrAG1。您的调试是否会以某种方式干扰?
  • 可以确认 - 方法按预期工作
  • 我忘了包含XmlReaderSettings 我已经更新了问题,设置IgnoreWhitespace=false 行为正常,但设置为true 会导致跳转。

标签: c# .net xmlreader


【解决方案1】:

问题在于,如果没有中间空格,则对XNode.ReadFrom() 的调用将使 XML 读取器定位在下一个元素处。然后while 条件在我们检查它之前立即消耗这个元素。解决方法是不要在之后立即调用XmlReader.Read(),而是继续检查节点(因为读取已经隐式完成):

while (reader.Read()) {
    while (reader.NodeType == XmlNodeType.Element 
           && reader.Name.Equals(elementName, StringComparison.InvariantCulture)) {
        yield return XNode.ReadFrom(reader) as XElement;
    }
}

(如果不清楚,循环中的if已更改为while。)

【讨论】:

    【解决方案2】:
    public static IEnumerable<XElement> GetElement(this XmlReader reader, string elementName)
    {
        while (!reader.EOF)
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "book")
                yield return XNode.ReadFrom(reader) as XElement;
            else
                reader.Read();
    }
    

    【讨论】:

      【解决方案3】:

      代码跳过了所有其他的书标签,因为书标签紧跟在彼此之后。 read 方法将阅读器留在下一个书标签,然后 read 方法在阅读第二个书标签之前移动,跳过该元素。试试下面我开发的代码,它总是有效的。

              public static IEnumerable<XElement> GetElement(XmlReader reader, string elementName)
              {
                  List<XElement> books = new List<XElement>();
      
      
                  while (!reader.EOF)
                  {
                      if(reader.Name != "book")
                      {
                          reader.ReadToFollowing("book");
                      }
                      if(!reader.EOF)
                      {
                          books.Add((XElement)XElement.ReadFrom(reader));
                      }
                  }
                  return books;
              }
      

      【讨论】:

        【解决方案4】:

        正如其他人所说,XNode.ReadFrom 正在将您的阅读器推进到下一本书的打开标签 (如果它们之间没有空格) 然后 reader.Read 将推进到该标签的内部文本.

        查看这里了解更多信息:

        https://stackoverflow.com/a/26788230/3850405

        修复您的扩展方法:

        public static IEnumerable<XElement> GetElement(this XmlReader reader, string elementName)
        {
            reader.MoveToElement();
        
            reader.Read();
            while (!reader.EOF)
            {
                if (reader.NodeType == XmlNodeType.Element 
                    && reader.Name.Equals(elementName, StringComparison.InvariantCulture))
                {
                    yield return XNode.ReadFrom(reader) as XElement;
                }
                else
                {
                    reader.Read();
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-02
          • 1970-01-01
          • 2019-01-19
          • 1970-01-01
          • 2013-02-01
          • 2011-09-25
          • 1970-01-01
          • 2023-03-03
          相关资源
          最近更新 更多