熟悉XPath的人都知道有些轴,比如preceding::
,是反向轴。而且,如果您将位置谓词放在使用反向轴构建的表达式上,您可能会向后而不是向前计数。例如
$foo/preceding-sibling::*[1]
返回 $foo
之前的前一个同级元素,而不是第一个前同级元素(按文档顺序)。
但是随后您会遇到似乎违反此规则的变体,这取决于位置谓词与反向轴的距离。例如
($foo/preceding-sibling::*)[1]
从文档的开头向前计数,而不是从 $foo
向后计数。
今天我写了一些代码,其中有一个表达式
$foo/preceding::bar[not(parent::baz)][1]
我想从 $foo
开始倒数。但是我的位置谓词是不是离 preceding::
轴太远了?在我添加 [1]
之前表达式是否丢失了反向?我想这可能行不通,所以我把它改成了
$foo/preceding::bar[not(parent::baz)][last()]
但后来我不太确定方向,所以我用括号来确定:
($foo/preceding::bar[not(parent::baz)])[last()]
但是,额外的括号有点令人困惑,我认为表达式可能效率较低,如果它真的必须从(大)输入文档的开头开始计数而不是从 $foo向后计数的话
。真的有必要这样做吗?
最后我测试了原始表达式,令我惊讶的是它起作用了!所以中间的 [not(parent::baz)]
并没有导致表达式失去它的反方向。
那个问题已经解决了,但我已经到了这样的地步,我想更好地处理何时我可以预期轴的反向应用。我的问题是:使用反向轴的 XPath 表达式在什么时候失去了它的反向方向?
我相信我现在已经找到了答案,所以我会回答我自己的问题。但是我找不到关于 SO 的答案,这件事困扰了我很长时间,值得在这里提问和回答。
最佳答案
我找到的最佳答案是旧的 email by Evan Lenz .
值得全文阅读,解释 XPath 在这方面的工作原理,以及 XPath 1.0 规范如何向我们展示答案。但执行摘要在这条规则中:
Step ::= AxisSpecifier NodeTest Predicate*
| AbbreviatedStep
Step 产生式定义了location step 的语法,并且只有在 location step 内轴的反向应用。 除了 nodetest 和谓词之外,轴和位置谓词之间的任何语法都会打断链条,方向将恢复为正向。
这解释了为什么如果您在 preceding::foo
周围加上括号并在括号外附加一个位置谓词,则位置谓词会忽略 preceding::
的方向> 轴。
这也解释了为什么我今天在我的代码中的第一次尝试成功了,尽管我的期望是:您可以根据需要在 NodeTest 之后放置任意数量的谓词,并且轴的方向仍然适用于所有谓词。
关于xpath - 究竟什么时候轴的反向适用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18524096/