选择没有XSLT / XPath密钥的节点

时间:2011-12-12 22:35:31

标签: xslt xpath

我使用Muenchian方法对XML文档中的节点进行分组。

作为输出的一部分,我想选择我分配密钥的所有节点。

我已经尝试了

<xsl:apply-templates select="*[key('kcWWPN','')]"/>

但这似乎没有正常工作,因为它没有选择任何节点。

有关正确方法的任何建议吗?

2 个答案:

答案 0 :(得分:5)

  

作为输出的一部分,我想选择我所有的节点   没有分配密钥。

好问题,+ 1。

以下是两种不同但简单的解决方案

为简单起见,我假设我们只键入元素,但下面的两个解决方案自然可以扩展到其他类型的节点。

<强>予。只需定义一个 计数器密钥

<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:key name="kDByClass" match="d" use="@class"/>

 <xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>

 <xsl:template match="*[key('kCounterKey', .)]">
  Counter Keyed: <xsl:value-of select="name()"/>

  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

将此转换应用于以下XML文档(从@lwburk借用):

<root>
    <d class="test">1</d>
    <d class="test">2</d>
    <d class="something">1</d>
    <q>3</q>
</root>

产生了想要的正确结果

  Counter Keyed: root
  Counter Keyed: q

II。 使用所有元素和所有键控元素的设置差异(后者使用Muenchian分组找到):

这个解决方案比第一个解决方案简单,因为不需要组成一个反键:

<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:key name="kDByClass" match="d" use="@class"/>

 <xsl:key name="kCounterKey" match="*[not(self::d)]" use="."/>

 <xsl:variable name="vKeyedValues" select=
 "//*[generate-id()
     = generate-id(key('kDByClass', @class)[1])
     ]
      /@class
  "/>

  <xsl:variable name="vKeyedElements" select=
   "key('kDByClass', $vKeyedValues)"/>

  <xsl:variable name="vNonKeyedElements" select=
  "//*[not(count(.|$vKeyedElements) = count($vKeyedElements))]
  "/>

 <xsl:template match="/">
  <xsl:for-each select="$vNonKeyedElements">
    Not Keyed: <xsl:value-of select="name()"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

将此转换应用于同一XML文档(上图)时,再次生成相同的正确结果

Not Keyed: root
Not Keyed: q

注意:当密钥不是节点时,最后一个解决方案可能不起作用,而是函数的结果,例如substring-before()。在这种情况下,我们只需修改原始密钥,因此其use属性只是:use="."并将此解决方案与修改后的原始密钥一起使用。可以证明这个过程产生了正确的,想要的元素集。

答案 1 :(得分:2)

您只需为要选择的节点重新创建已使用的密钥,并尝试检索该密钥的值。如果为该键返回的集合不包含当前节点,则应选择它。

例如,假设密钥是使用每个元素的class属性值及其字符串值的串联创建的。我们可以使用以下选项选择文档中没有指定键的所有元素:

<xsl:apply-templates
            select="//*[not(count(.|key('byClass', concat(@class, '|', .)))=
                            count(key('byClass', concat(@class, '|', .))))]"/>

这是一个完整的演示:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:key name="byClass" match="d" use="concat(@class, '|', .)"/>
    <xsl:template match="/">
        <xsl:apply-templates
            select="//*[not(count(.|key('byClass', concat(@class, '|', .)))=
                            count(key('byClass', concat(@class, '|', .))))]"
            mode="test"/>
    </xsl:template>
    <xsl:template match="*" mode="test">
        <xsl:value-of
            select="concat('Node ', local-name(), 
               ' not assigned a key', '&#xa;')"/>
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="text()"/>
</xsl:stylesheet>

应用于此输入:

<root>
    <d class="test">1</d>
    <d class="test">2</d>
    <d class="something">1</d>
    <q class="test">1</q>
</root>

它产生:

Node root not assigned a key
Node q not assigned a key

请注意,q生成的密钥是映射到文档中一组节点的实际密钥,但此元素不在key('byClass', 'test|1')返回的集合中,所以我们说这个节点尚未分配密钥。

另请注意,空字符串('')是一个完全有效的密钥,这就是为什么这不能达到你希望的那样:

<xsl:apply-templates select="*[key('kcWWPN', '')]"/>