【问题标题】:XPath expression for finding an element without a particular attribute用于查找没有特定属性的元素的 XPath 表达式
【发布时间】:2012-02-14 19:25:45
【问题描述】:

我有一个格式如下的xml文件

<someXML>
    <a>
        <b></b>
        <b test='someValue'></b>
    </a>

    <c>
        <d test='someOtherValue'></d>
        <d></d>
    </c>
</someXML>

我正在尝试编写 C# 代码,该代码将为我提供一个仅包含具有 test 属性的节点的节点集。我正在尝试以这种方式使用 XPathExpression:

XDocument document = XDocument.Load("someXML");
XPathNavigator navigator = document.CreateNavigator();
XPathExpression expression = XPathExpression.Compile("//*[@test]");
object nodeSet = navigator.Evaluate(expression);

这似乎不起作用。当我调试时,nodeSet 总是包含 Root 元素。表达式有问题还是正常工作?

谢谢!

【问题讨论】:

    标签: c# xpath


    【解决方案1】:

    您是否认为nodeSet 包含“Root”元素,因为调试器将其字符串表示形式显示为Position=0, Current={Root}?这可能会产生误导;出现“Root”元素的原因是XPathNodeIterator(在您的情况下是Evaluate 返回的实际类型)尚未定位在查询产生的第一个节点上。根据 XPathNodeIterator 的 MSDN 文档:

    XPathNavigator 类返回的 XPathNodeIterator 对象未位于选定节点集中的第一个节点上。必须调用XPathNodeIterator 类的MoveNext 方法才能将XPathNodeIterator 对象定位在选定节点集中的第一个节点上。

    您可以通过遍历 nodeSet 来检索路径项:

    XDocument document = XDocument.Load("someXML");
    XPathNavigator navigator = document.CreateNavigator();
    XPathExpression expression = XPathExpression.Compile("//*[@test]");
    XPathNodeIterator nodeSet = (XPathNodeIterator)navigator.Evaluate(expression);
    foreach (XPathItem item in nodeSet)
    {
        // Process item here
    }
    

    或者,如果您希望将它们转换为数组:

    XDocument document = XDocument.Load("someXML");
    XPathNavigator navigator = document.CreateNavigator();
    XPathExpression expression = XPathExpression.Compile("//*[@test]");
    XPathNodeIterator nodeSet = (XPathNodeIterator)navigator.Evaluate(expression);
    XPathItem[] items = nodeSet.Cast<XPathItem>().ToArray();
    

    【讨论】:

      【解决方案2】:

      您的 XPath 确实有效,只是您需要稍微不同地检索节点:

      XPathNavigator navigator = document.CreateNavigator();
      XPathExpression expression = XPathExpression.Compile("//*[@test]");
      var matchedNodes = navigator.Select(expression);
      foreach (XPathNavigator node in matchedNodes)
      {
          Console.WriteLine(node.UnderlyingObject);
      }
      

      上面的代码段将打印以下内容:

      <b test='someValue'></b>
      <d test='someOtherValue'></d>
      

      【讨论】:

        【解决方案3】:

        您的表达似乎是正确的。但是您可以使用XPathSelectElements 扩展方法来更简单:

        var nodes = document.XPathSelectElements("//*[@test]");
        

        【讨论】:

          【解决方案4】:

          您的 XPath 查询是正确的。 Current 成员将是 Root 元素(我不是 XPathIterator 专家,所以我真的不知道它背后的基本原理)。如果您遍历您的 nodeSet,您将只获得与您的 XPath 查询匹配的内容。所以我想答案是迭代集合并将每个项目放入一个集合中。

          【讨论】:

            【解决方案5】:

            我相信您会想要使用 XPathNavigator 对象的 Select 方法,而不是 Evaluate,它会返回一个 XPathNodeIterator 对象。

            http://msdn.microsoft.com/en-us/library/3tk3f03k.aspx

            或者您可以使用 XmlDocument 对象和 XmlNodeList:

                    XmlDocument document = new XmlDocument();
                    document.Load("somexml.xml");
            
                    XmlNodeList nodeList = document.SelectNodes("//*[@test]");
            
                    foreach (XmlNode Node in nodeList) { 
                        // do something.            
                    }
            

            【讨论】:

              【解决方案6】:

              为什么不这样做,假设您只想要节点并且没有使用导航器的特定原因(省略了空检查):

              var document = XDocument.Load("someXML");
              var myNodes = document.Descendants().Where(node => node.Attribute(myAttributeName) != null);
              

              它不使用导航器,但如果您只需要节点,它非常简单明了。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2014-12-09
                • 1970-01-01
                • 1970-01-01
                • 2015-01-27
                • 2019-07-27
                • 1970-01-01
                • 2012-10-28
                • 1970-01-01
                相关资源
                最近更新 更多