【问题标题】:Is there a backwards XML parser for .NET?.NET 是否有向后的 XML 解析器?
【发布时间】:2010-01-04 11:12:28
【问题描述】:

在我的应用程序中,我对 XML 字符串有一个已知的兴趣偏移量,并且想要回答诸如“我的父元素是什么?”之类的问题。无需解析整个文档。

article 提到了一个似乎在 Objective-C 中用于“向后”XML 解析的库。我的应用程序不需要完整的 XML 支持,所以我很乐意忍受所有关于无法完全稳健解析的警告。 C#/.NET 有类似的东西吗?

澄清:我不是在询问解析解决方案或一般性能权衡,我对特定情况感兴趣,即我在文本流中途的某个时间点,只需要了解有关本地结构的一些信息。想象一下我不想获取文档顶部的情况,因为访问的延迟非常高。

【问题讨论】:

    标签: .net xml parsing


    【解决方案1】:

    如果不对文本的性质做出一些重要假设,就不可能做到这一点。最值得注意的是,您必须假设它是格式良好的 XML,并且它既不包含 CDATA 部分也不包含命名空间。

    如果您从流中间的任何位置开始并返回,直到您点击似乎是元素的开头,您无法知道您正在查看的文本实际上是 元素的开始。它可能是 CDATA。在您回溯整个流以寻找 <![CDATA[ 并且没有找到它之前,您无法判断它不是 CDATA。

    命名空间存在类似的问题。如果您找到像<Foo 这样的开始标记,您无法确定Foo 在默认命名空间中,直到您一直回溯到文档的根元素并确定没有祖先元素具有命名空间声明.如果找到<x:Foo,则必须回溯,直到找到带有xmlns:x 声明的封闭元素。

    如果您确定文本是格式良好的 XML,它不包含 CDATA,并且它对命名空间的使用是有限的(即,您可以通过查看元素的开头来判断元素在哪个命名空间中标签),那么您尝试做的一些事情至少是可能的。

    您可以备份到您遇到的第一个开始标记,创建一个原点为该位置的StreamReader,并使用它创建一个设置为处理文档片段的XPathDocument。请注意,顺便说一下,您无法保证第一次使用 XPathDocument 时不会一直读到文本的末尾,除非您再次了解文本的性质并且您知道匹配的结束标签将会出现。

    但这不会处理您提到的特定情况,即查找父元素。要找到父元素,您需要找到一个匹配的结束标签之前没有(当您向后移动时)的开始标签。这并不难做到——你发现的每个< 字符都是开始标签、结束标签或空元素的开始,你可以将结束标签放在堆栈上并弹出它们当你找到他们匹配的开始标签时关闭。当您点击开始标签并且堆栈为空时,您位于父元素的开头。

    但这也是一个过程,可能会导致您一直回溯到流的源头,尤其是在您正在查找的 XML 是典型的低俗 XML 日志格式的琐碎情况下:

    <log>
       <entry>...</entry>
       <entry>...</entry>
    

    ...无限重复

    【讨论】:

      【解决方案2】:

      听起来像XPathDocument 可能就是您要找的。此类提供 XML 文档的快速、只读的内存表示。它不构建 DOM,并且针对 XPath 查询进行了优化。

      XPathDocument 也可用于解析 XML 片段。为此,您必须从将其一致性级别设置为片段的XmlReader 创建它。

      以下示例代码首先从 XML 片段中选择一组 XML 节点,然后根据 XPath 表达式选择每个节点的父节点:

      using System;
      using System.IO;
      using System.Xml;
      using System.Xml.XPath;
      
      class Program
      {
          static void Main(string[] args)
          {
              string xml = File.ReadAllText(@"C:\tmp\smplInput.xml");
      
              XmlReaderSettings xrs = new XmlReaderSettings();
              xrs.ConformanceLevel = ConformanceLevel.Fragment;
      
              using (TextReader textReader = new StringReader(xml))
              {
                  using (XmlReader xmlReader = XmlReader.Create(textReader, xrs))
                  {
                      // Create a new XPathDocument   
                      XPathDocument doc = new XPathDocument(xmlReader);
      
                      // Create navigator   
                      XPathNavigator navigator = doc.CreateNavigator();
      
                      // Set up namespace manager for XPath   
                      XmlNamespaceManager ns = new XmlNamespaceManager(navigator.NameTable);
                      ns.AddNamespace("w", "http://www.example.com/2010/");
      
                      // Select nodes  
                      XPathNodeIterator users = navigator.Select("//w:user", ns);
      
                      while (users.MoveNext())
                      {
                          XPathNavigator user = users.Current;
                          XPathNavigator department = user.SelectSingleNode("parent::node()", ns);
                          Console.WriteLine(string.Format("User {0} is in department {1}",
                              user.GetAttribute("name", ns.DefaultNamespace),
                              department.GetAttribute("type", ns.DefaultNamespace)));
                      }
                  }
              }
          }
      }
      

      要尝试代码,您可以使用以下 XML 输入文档:

      <?xml version="1.0" encoding="utf-8" ?>
      <w:departments xmlns:w="http://www.example.com/2010/">
        <w:department type="A">
          <w:user name="w" />
          <w:user name="x" />
          <w:department type="B">
            <w:user name="x" />
            <w:user name="y" />
          </w:department>
          <w:department type="C">
            <w:user name="x" />
            <w:user name="y" />
            <w:user name="z" />
          </w:department>
        </w:department>
        <w:department type="D">
          <w:user name="w" />
        </w:department>
      </w:departments>
      

      【讨论】:

      • 你能举例说明我如何使用它来实现这一点吗?在使用 XPathDocument 的正常方式中,我仍然将整个字符串传递给它,但没有指出该字符串解析应该从哪里开始。
      • 我添加了一个例子。只需传递整个字符串并执行 XPath 查询来选择感兴趣的节点。依赖文本偏移似乎不是一个好主意。在大多数情况下,XPathDocument 应该以合理的性能运行。因此,在尝试编写自己的解析器之前,我会尝试一下,看看您是否获得足够快的结果(编写自己的解析器似乎有点像过早的优化)。另请注意,通过微调 XPath 查询可能会优化性能。
      • 好的,所以 XPathDocument 不是我要寻找的——它不是关于一般的速度或效率,而是关于我知道我想在文本中从哪里开始的特定情况,并且我想要完全避免看其他地方。例如,我有一个文件的一部分,而获取它的任何其他部分都需要像磁带机器人一样进入高延迟存储。
      • @jcs: XPathDocument 也可用于解析 XML 片段。唯一的条件是它是一个格式良好的片段,即每个开始标签必须在同一级别上具有相应的结束标签。查看我的更新示例。
      【解决方案3】:

      另一种方法是解析一次XML,然后生成XML索引,这样下次加载索引时就不需要重复解析XML了……看下面的文章

      http://xml.sys-con.com/node/453082

      【讨论】:

      • 吉米,请注意有些问题不是 VTD 的答案。这是其中之一。这是一个有用的链接...meta.stackexchange.com/questions/21823/what-constitutes-spam
      • 提出一般方法而不是具体答案并没有错……为什么该链接甚至相关??
      • @kdt:根据您提出的问题,不清楚 VTD 是否不是一种有趣的方法。 “我正处于文本流的中途,只需要了解有关本地结构的一些信息。”如果您已经创建了索引,VTD 似乎可以满足这一要求。而且无论您是否打算使用 VTD,对您的 XML 进行预索引可能会解决您的实际问题。也许您需要在问题中提供更多详细信息,因为到目前为止似乎所有答案都不符合您的要求。
      • 请注意,张先生是VTD-XML的作者。
      【解决方案4】:

      xponentsoftware 的 CAX 完全符合您的要求。

      【讨论】:

        猜你喜欢
        • 2013-02-06
        • 1970-01-01
        • 2011-06-28
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        • 1970-01-01
        • 2010-12-03
        • 2010-11-07
        相关资源
        最近更新 更多