XSLT,XPath唯一子节点唯一的问题是根本没有选择非唯一节点

时间:2010-12-01 06:30:44

标签: xslt xpath

<root>
  <parent>
    <child>
     <name>John</name>
    </child>
    <child>
      <name>Ben</name>
    </child>
  </parent>
  <parent>
    <child>
     <name>John</name>
    </child>
    <child>
     <name>Mark</name>
    </child>
    <child>
      <name>Luke</name>
    </child>
 </parent>
</root>

我只想要唯一的子节点,即只有一个子节点,如果有多个子节点具有相同的名称。

如:

John Ben Mark Luke

我试过了:

<xsl:for-each select="parent">
  <xsl:for-each select="child[name != preceding::name]">
    <xsl:value-of select="name"/>
  </xsl:for-each>
</xsl:for-each>

但我明白了:

Ben Mark Luke

?!

1 个答案:

答案 0 :(得分:7)

您的问题是您使用!=运算符来比较值和节点集

这是错误的 - 始终避免使用!=运算符,并始终使用not()函数和=运算符时,其中一个操作数比较是一个节点集。

以下是正确的解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*">
    <xsl:for-each select="parent">
      <xsl:for-each select="child[not(name = preceding::name)]">
        <xsl:value-of select="concat(name, ' ')"/>
      </xsl:for-each>
    </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档

<root>
  <parent>
    <child>
     <name>John</name>
    </child>
    <child>
      <name>Ben</name>
    </child>
  </parent>
  <parent>
    <child>
     <name>John</name>
    </child>
    <child>
     <name>Mark</name>
    </child>
    <child>
      <name>Luke</name>
    </child>
 </parent>
</root>

产生了想要的正确结果

John Ben Mark Luke 

解释:以下是W3C XPath 1.0规范如何定义!=运算符的语义:

  

“如果要比较的一个对象是a   node-set和另一个是字符串,   那么比较将是真的,如果   并且只有在节点中有节点   node-set这样的结果   进行比较   节点的字符串值和另一个   字符串是真的。“

这意味着

's' != node-set
如果node-set中只有一个节点不等于's',则

始终为真。

这不是想要的语义。

另一方面,

not('s' = node-set()) 
仅当node-set中的节点不等于's'时,

才为真。

这正是想要的比较。

请注意:您选择的分组技术是O(N ^ 2),并且只能用于要进行重复数据删除的非常小的值集。如果需要效率,一定要使用Muenchian方法进行分组(讨论或演示这不属于本问题的范围)。