如何使用XSLT为给定属性的每个值选择第一个节点?

时间:2011-03-10 17:23:06

标签: xslt xpath xslt-1.0

我有一个看起来像这样的文件:

<template>
 <otherstuff>
 <group name="foo">
 ...stuff...
 </group>
</template>
<template>
 <otherstuff>
 <group name="bar">
 ...different stuff...
 </group>
</template>
<template>
 <otherstuff>
 <group name="foo">
 ...same stuff as first foo group...
 </group>
</template>

我想要做的是为给定组名的每个实例调用一个特定的模板 - 所以一次用于“foo”,一次用于“bar”。由于它们是相同的,我不关心它们中的哪一个用于调用此模板。

根据Select unique nodes based on a combination of two attribute values的答案,我制作了以下代码:

<xsl:key name="groupsByName" match="//group" use="@name"/>

<xsl:template match="/">
    <xsl:for-each select="//group[count(.|key('groupsByName',@name)[1])!=1]">
        <xsl:call-template name="template-or-group"/>
    </xsl:for-each>
</xsl:template>

然而,结果是不调用模板。我也怀疑,因为我的案例比我的代码基于的问题更简单,所以可能有一种更简单的方法。但是,我的XSLT不够强大,无法确定它可能是什么。

2 个答案:

答案 0 :(得分:1)

我建议用以下代码替换你的模板:

<xsl:template match="group[generate-id(.) = generate-id(key('groupsByName',@name)[1])]">
  ....
</xsl:template>

或者只是将xpath表达式更改为//group[generate-id(.) = generate-id(key('groupsByName',@name)[1])]

count(.|some-node-set)通常用于检查某个节点是否属于该集合。在这里,您需要将当前节点与每个组的第一个唯一节点进行比较。此外,改进Xpath表达式并仅匹配具有@name属性的组元素可能也很有用。说完所有这些后,我们得到://group[@name][generate-id(.) = generate-id(key('groupsByName', @name)[1])]

答案 1 :(得分:1)

只需替换

//group[count(.|key('groupsByName',@name)[1])!=1]

<强>与

//group[count(.|key('groupsByName',@name)[1])=1]

这是一个完整的解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="groupsByName" match="//group"
      use="@name"/>

 <xsl:template match="/">
  <xsl:for-each select=
   "//group[count(.|key('groupsByName',@name)[1])=1]">
   <xsl:call-template name="template-or-group"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template name="template-or-group">
  <xsl:value-of select="@name"/>
  <xsl:text>&#xA;</xsl:text>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档(已修复为格式良好):

<t>
    <template>
        <otherstuff/>
        <group name="foo">  ...stuff...  </group>
    </template>
    <template>
        <otherstuff/>
        <group name="bar">  ...different stuff...  </group>
    </template>
    <template>
        <otherstuff/>
        <group name="foo">  ...same stuff as first foo group...  </group>
    </template>
</t>

产生了想要的正确结果:

foo
bar