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 Transformation (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>

これに関する主な問題は、各チーム内だけでなく、すべてのチームのすべてのプレーヤーで重複が発生することです。

上記の例では、1 つのオカレンスのみを返す必要があります (チーム 1 のプレイヤー DG)。

マイナーな問題: これは重複の 2 番目のオカレンスのみを出力します。できれば、2 回目、3 回目、4 回目... の出現を出力する必要があります (1 回目はスキップできます)。これは、キー機能の後の「[2]」が原因であることがわかっています。私が見つけたほとんどの例は、重複を削除する方法に関するものでした. おそらくこれを達成するためのより良い方法があります...

どんな助けでも大歓迎です。

ありがとう、ブルーノ

4

2 に答える 2

3

あなたは正しい軌道に乗っています!@lastname必要な主な変更は、キーの調整です。特に、値<team>親要素に関する一意の情報を考慮する必要があります。

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

もう 1 つの変更点は、すでに指摘したとおりです。すべての重複[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 に答える