/tt/b[@text != ../a/@text] 等答案是错误的,选择了错误的节点集:
<b text="14">data</b>
<b text="2">data</b>
如我们所见,第二个选定节点的text 属性为2,有一个a 元素,其text 属性为2。
这是一个正确的 XPath 表达式:
/tt/b[not(@text = ../a/@text)]
根据提供的 XML 文档进行评估时:
<tt>
<a text="1"/>
<a text="2"/>
...
<a text="n"/>
<b text="14">data</b>
<b text="2">data</b>
...
</tt>
它只正确选择了一个节点:
<b text="14">data</b>
解释:
根据定义 the XPath != operator 至少有一个参数是节点集时具有非常不直观的行为:
来自W3C XPath 1.0 Recommendation:
"如果要比较的一个对象是一个节点集,另一个是一个数字,
那么当且仅当在
节点集,使得对
要比较的数字和转换字符串值的结果
使用 number 函数将该节点转换为数字为真。如果一个
要比较的对象是一个节点集,另一个是一个字符串,那么
当且仅当在
节点集,使得对
节点的字符串值和其他字符串为真"
在这个元素的特殊情况下:
<b text="2">data</b>
比较:
@text != ../a/@text
是true(),即使存在:
<a text="2"/>
因为至少存在一个../a元素(实际上不止一个),其text属性的字符串(或数字)值不等于"2"。
这是众所周知的事实和常见问题解答:除非您完全知道自己在做什么,否则请始终避免使用 != 运算符!
这个问题的正确解决方法是像这样使用not()函数:
not(@text = ../a/@text)
仅当 @text = ../a/@text 为 false() 时,此表达式才计算为 true() —— 即仅当甚至没有一个 ../a/@text 的字符串值等于 text 属性的字符串值时上下文节点。
基于 XSLT 的验证:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="/tt/b[not(@text = ../a/@text)]"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档(上图)时,会产生正确的结果:
<b text="14">data</b>