如何选择唯一的节点

时间:2008-10-22 22:10:20

标签: xml xslt

我发现this page描述了Muenchian方法,但我认为我错误地应用了它。

考虑到这将返回一组年龄:

/doc/class/person/descriptive[(@name='age')]/value
  

1..2..2..2..3..3..4..7

但我希望每个年龄段的节点集只有一个节点。

  

1..2..3..4..7

其中每一个似乎都返回所有值,而不是唯一值:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::value)]/value
/doc/class/person/descriptive[(@name='age')]/value[not(value=preceding-sibling::value)]

我错过了什么?

5 个答案:

答案 0 :(得分:23)

以下是一个例子:

<root>
    <item type='test'>A</item>
    <item type='test'>B</item>
    <item type='test'>C</item>
    <item type='test'>A</item>
    <item type='other'>A</item>
    <item type='test'>B</item>
    <item type='other'>D</item>
    <item type=''>A</item>
</root>

XPath:

//preceding::item/preceding::item[not(.=preceding-sibling::item)]/text()

结果: A B C D

修改: 正如mousio评论的那样,如果它出现的唯一时间,则不会捕获列表中的最后一项。考虑到这一点和Fëanor的评论,这是一个更好的解决方案:

/root/item[not(.=preceding-sibling::item)]

答案 1 :(得分:14)

以下是使用他的数据的BQ答案的Muenchian版本:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes" method="text"/>
  <xsl:key name="item-by-value" match="item" use="."/>

  <xsl:template match="/">
    <xsl:apply-templates select="/root/item"/>
  </xsl:template>

  <xsl:template match="item">
    <xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))">
      <xsl:value-of select="."/>
      <xsl:text>
</xsl:text>
    </xsl:if>
  </xsl:template>

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

此转换提供

一个
  乙
  ç
  d

  1. key()模板中的item查找返回一个节点集,其中包含与上下文节点具有相同字符串值的所有item元素。
  2. 如果将需要单个节点的函数应用于节点集,它将在该节点集中的第一个节点上运行。
  3. generate-id()的所有调用都保证在单次传递文档期间为给定节点生成相同的ID。
  4. 因此,如果上下文节点与key()调用返回的第一个节点相同,则测试将为真。

答案 2 :(得分:3)

对于那些仍在XSLT中寻找select的人:

使用XSLT 2.0, 您可以使用 “不同的值(/ DOC /类/人/描述[(@名= '年龄')] /值)”

答案 3 :(得分:2)

Muenchian方法使用键从节点集创建唯一的项列表。对于您的数据,密钥将如下所示:

<!-- Set the name to whatever you want -->
<xsl:key name="PeopleAges" match="/doc/class/person/descriptive[@name = 'age']/value" use="." />

从那里开始,我个人会使用xsl:apply-templates,但您可以在其他地方使用以下select属性:

<!-- you can change `apply-templates` to: `copy-of` or `for-each`. -->
<xsl:apply-templates select="/doc/class/person/descriptive[@name = 'age']/value[count(. | key('PeopleAges', .)[1]) = 1]" />

上述相应的匹配要简单得多:

<xsl:template match="person/descriptive[@name = 'age']/value">
    <strong>Age: </strong><xsl:value-of select="." />
</xsl:template>

答案 4 :(得分:1)

您是否在前一个值之后错过了对“描述性”的引用?有些事情如下:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value

(尚未测试过)