XPath表达式

3

我有这样一个XML文件:

<lib:library>    
    <lib:book> XML </lib:book>
    <lib:book> XPath </lib:book>
    <lib:book> XSLT </lib:book>
    <lib:book> Java </lib:book>
    <lib:book> C++ </lib:book>    
</lib:library>

我想要去到第二本书,可以使用类似于//lib:Book[2]的方法...它是有效的。但如果在同一个XML文件中出现了相同标签名但不同命名空间的情况,这时我的XPath表达式就无法正常工作...

我可以使用以下方式进行替换:

//*[local-name() = "book"]

这个表达式返回了XML文件中所有包含书籍的标签...但如果我想要得到第二本书,应该如何重写XPath表达式并添加关于数字的条件呢?当然,我不想考虑命名空间,它必须适用于所有使用的命名空间。

谢谢 Luca


好问题,+1。这是一个常见问题。你知道吗,目前你选择了错误的答案吗? - Dimitre Novatchev
2个回答

4

当前选择的答案是错误的

事实上,//someExpression[2] 可以选择多个节点。

例如,如果我们有以下 XML 文档

<lib:library xmlns:lib="UNDEFINED!!!">
  <topic name="XML">
      <lib:book> XML </lib:book>
  </topic>
  <topic name="XPath">
      <lib:book> XPath </lib:book>
  </topic>
  <topic name="XSLT">
     <lib:book> XSLT1 </lib:book>
     <lib:book> XSLT2 </lib:book>
  </topic>
  <topic name="Imperative PLs">
     <lib:book> Java </lib:book>
     <lib:book> C++ </lib:book>
  </topic>
</lib:library>

当表达式为:

   //*[local-name() = "book"][2]

在与上面的文档进行比较时,选择了两个节点(但它们都不是文档中具有所需属性的第二个节点):

<lib:book xmlns:lib="UNDEFINED!!!"> XSLT2 </lib:book>
<lib:book xmlns:lib="UNDEFINED!!!"> C++ </lib:book>

解决方案:选择整个文档中第N(例如第二个)个节点(例如lib:book)的一种方法是:

   (//*[local-name() = "book"])[2]

当对上述文档进行此表达式评估时,将选择正确的单个节点:
<lib:book xmlns:lib="UNDEFINED!!!"> XPath </lib:book>
说明: 根据W3C XPath建议的定义:

//缩写为/descendant-or-self::node()/

因此:

//someName[2]

是以下内容的简写:

/descendant-or-self::node()/someName[2]

这会选择文档中名称为someName并且是其父级的第二个someName子元素的任何元素。

换句话说,[]操作符比//伪操作符绑定得更强(优先级更高)。这就是为什么我们需要使用方括号来覆盖默认操作符优先级。


0

那么//*[local-name() = "book"][2]怎么样?应该可以工作。

正如评论中所指出的,除非你确信自己知道在做什么,否则应避免使用//


是的,它有效...那么这个表达式呢?//*[local-name() = 'book'][1]/[local-name() = 'author'][1] 假设书可以有多个作者作为子元素。 - Luca
@luca:我会说这个表达式返回第一本书的第一个作者。 - MarcoS
1
这个答案是错误的。实际上,//expression[2] 可以选择 许多 节点。在使用 XPath 的 // 伪操作符时应该小心。 - Dimitre Novatchev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接