4

我是 XSLT/XML 的新手。

我有一个与此类似的 XML 文件:

<event>
  <division name="Div1">
    <team name="Team1">
      <player firstname="A" lastname="F" />
      <player firstname="B" lastname="G" />
      <player firstname="C" lastname="H" />
      <player firstname="D" lastname="G" />
    </team>
    <team name="Team2">
      <player firstname="A" lastname="F" />
      <player firstname="B" lastname="G" />
      <player firstname="C" lastname="H" />
      <player firstname="D" lastname="I" />
    </team>
  </division>
</event>

我正在尝试编写一个 XSL 转换(与 xsltproc 一起使用)来为我提供同一团队中具有相同姓氏的球员的姓名。

在四处搜索后,我想到了这个:

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

  <xsl:key name="lastnames" match="player" use="@lastname" />

  <xsl:template match="/">    
    <xsl:for-each select="event/division/team">

      <xsl:variable name="dups" select="player[generate-id() = generate-id(key('lastnames', @lastname)[2])]" />

      <xsl:if test="$dups">
        Team: <xsl:value-of select="@name" /> (<xsl:value-of select="../@name" />)
        Players: 
        <xsl:for-each select="$dups">
          <xsl:value-of select="@lastname" />, <xsl:value-of select="@firstname" />.
        </xsl:for-each>
      </xsl:if>

    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

这样做的主要问题是它给了我所有球队的所有球员的重复,而不仅仅是每支球队。

在上面的示例中,它应该只返回一次(对于 Team1 中的玩家 DG)。

小问题:这只会打印第二次出现的重复项。最好它应该打印第 2、3、4... 次出现(第 1 次可以跳过)。我知道这是因为键功能后面的“[2]”。我发现的大多数示例都是关于如何删除重复项,这里我需要相反的,所以这是我发现给我(接近)我需要的技巧。可能有更好的方法来实现这一点......

任何帮助表示赞赏。

谢谢,布鲁诺

4

2 回答 2

3

你在正确的轨道上!所需的主要更改是对键的调整 - 特别是需要考虑有关父元素的@lastname唯一信息:<team>

<xsl:key
  name="kPlayerByLastnameAndTeam"
  match="player"
  use="concat(parent::team/@name, '+', @lastname)" />

要进行的另一项更改是您已经注意到的:您需要除[2]谓词之外的其他内容来获取所有重复项。诀窍是在属性中使用相同的键,@match以便选择所有其他元素:

key(
  'kPlayerByLastnameAndTeam',
  concat(parent::team/@name, '+', @lastname))
[not(generate-id() = generate-id(current()))]

要查看所有这些操作,请查看这个完整的解决方案。

当这个 XSLT:

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

  <xsl:key
    name="kPlayerByLastnameAndTeam"
    match="player"
    use="concat(../@name, '+', @lastname)"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="team">
    <xsl:apply-templates
      select="*[
                generate-id() =
                generate-id(key(
                  'kPlayerByLastnameAndTeam',
                  concat(../@name, '+', @lastname))[1])
               ]"/>
  </xsl:template>

  <xsl:template match="player">
    <xsl:variable
      name="vDups"
      select="key(
                'kPlayerByLastnameAndTeam',
                concat(../@name, '+', @lastname))
              [not(generate-id() = generate-id(current()))]"/>
    <xsl:if test="$vDups">
      <xsl:value-of
        select="concat('Team: ', ../@name, ' (', ../../@name, ')')"/>
      <xsl:text>&#10;Players: </xsl:text>
      <xsl:apply-templates select="$vDups" mode="copy"/>
      <xsl:text>&#10;&#10;</xsl:text>
    </xsl:if>
  </xsl:template>

  <xsl:template match="player" mode="copy">
    <xsl:if test="position() &gt; 1">; </xsl:if>
    <xsl:value-of select="concat(@lastname, ', ', @firstname, '.')"/>
  </xsl:template>

</xsl:stylesheet>

...适用于提供的 XML:

<event>
  <division name="Div1">
    <team name="Team1">
      <player firstname="A" lastname="F"/>
      <player firstname="B" lastname="G"/>
      <player firstname="C" lastname="H"/>
      <player firstname="D" lastname="G"/>
    </team>
    <team name="Team2">
      <player firstname="A" lastname="F"/>
      <player firstname="B" lastname="G"/>
      <player firstname="C" lastname="H"/>
      <player firstname="D" lastname="I"/>
    </team>
  </division>
</event>

...产生了想要的结果:

Team: Team1 (Div1)
Players: G, D.
于 2013-06-04T04:28:21.123 回答
1

您也可以通过以下方式实现:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>
  <xsl:key name="lastnames" match="player" use="@lastname"/>

  <xsl:template match="event">
    <xsl:text>Team:&#10;</xsl:text>
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="team">
    <xsl:text>&#10;</xsl:text>
    <xsl:value-of select="concat(@name,' (',parent::division/@name,')')"/>
    <xsl:text> Players: </xsl:text>
    <xsl:for-each select="player[@lastname = preceding-sibling::player/@lastname]">
      <xsl:value-of select="concat(@lastname,', ', @firstname,'.')"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

输出:

Team:

Team1 (Div1) Players: G, D.
Team2 (Div1) Players: 
于 2013-06-04T04:41:06.980 回答