我使用Muenchian方法对XML文档中的节点进行分组。
作为输出的一部分,我想选择我不分配密钥的所有节点。
我已经尝试了
<xsl:apply-templates select="*[key('kcWWPN','')]"/>
但这似乎没有正常工作,因为它没有选择任何节点。
有关正确方法的任何建议吗?
答案 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', '
')"/>
<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', '')]"/>