XPath/XSLT:如何选择满足涉及另一组元素的条件的所有元素

2 xml xslt xpath

我有一个类似于以下的XML文档:

<tt>
  <a text="1"/>
  <a text="2"/>
  ...
  <a text="n"/>

  <b text="14">data</b>
  <b text="2">data</b>
  ...
</tt>
Run Code Online (Sandbox Code Playgroud)

我怎样才能选择所有<b>那些元素text属性不等于text任何属性<a>的元素?我正在使用XPath 1.0.

我在考虑类似的东西tt/b[not (tt/a[@text = xxx::@text])],xxx应该在哪里引用tt/b被检查的元素.我不知道它究竟是如何完成的.

Dim*_*hev 6

答案如/tt/b[@text != ../a/@text]错,并选择错误的节点集:

<b text="14">data</b>
<b text="2">data</b>
Run Code Online (Sandbox Code Playgroud)

正如我们看到的,第二个选择的节点的text属性2和存在一种a元素,它的text属性2.

这是一个正确的XPath表达式:

/tt/b[not(@text = ../a/@text)]
Run Code Online (Sandbox Code Playgroud)

根据提供的XML文档进行评估时:

<tt>
  <a text="1"/>
  <a text="2"/>
  ...
  <a text="n"/>

  <b text="14">data</b>
  <b text="2">data</b>
  ...
</tt>
Run Code Online (Sandbox Code Playgroud)

它只能正确选择一个节点:

<b text="14">data</b>
Run Code Online (Sandbox Code Playgroud)

说明:

根据定义,!=只要其至少一个参数是节点集,XPath 运算符就会有非常不直观的行为:

W3C XPath 1.0建议:

"如果要比较的一个对象是节点集而另一个是数字,那么当且仅当节点集中有节点时才进行比较,以便对数字进行比较的结果比较和使用数字函数将该节点的字符串值转换为数字的结果为真.如果要比较的一个对象是节点集而另一个是字符串,那么比较将为真,如果并且只有在节点集中有一个节点,以便对节点的字符串值和另一个字符串执行比较的结果为真"

在这个元素的特殊情况下:

<b text="2">data</b>
Run Code Online (Sandbox Code Playgroud)

比较:

@text != ../a/@text
Run Code Online (Sandbox Code Playgroud)

true()即使存在:

<a text="2"/> 
Run Code Online (Sandbox Code Playgroud)

因为至少存在一个../a元素(实际上多于一个),其text属性不等于的字符串(或数字)值"2".

这是众所周知的事实和常见问题解答:!=除非您完全知道自己在做什么,否则请务必避免使用操作员!

这个问题的正确解决方案是使用这样的not()函数:

not(@text = ../a/@text)
Run Code Online (Sandbox Code Playgroud)

此表达式true()仅计算为@text = ../a/@textis false()- 即使只有一个单独../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>
Run Code Online (Sandbox Code Playgroud)

当在提供的XML文档(上面)上应用此转换时,会生成正确的结果:

<b text="14">data</b>
Run Code Online (Sandbox Code Playgroud)