2

私は4つのステップで構成されるXSL変換に取り組んでいます。私は個々のステップを理解しましたが、それらを組み合わせる方法に固執しています。これが私がやりたいことです:

ソースXMLドキュメント:

<application>
    <contactPerson>
        <name>Dominik</name>
        <countryCode>DE,SP</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Andrea</name>
        <countryCode>FR</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Alex</name>
        <countryCode>FR,DE</countryCode>
    </contactPerson>
    <contactPerson>
        <name>Andre</name>
        <countryCode>FR</countryCode>
    </contactPerson>
</application>

ターゲットXMLドキュメント:

<application>
    <memberState>
        <countryCode>DE</countryCode>
        <contactPerson>
            <name>Dominik</name>
        </contactPerson>
        <contactPerson>
            <name>Dorothea</name>
        </contactPerson>
    </memberState>
    <memberState>
        <countryCode>FR</countryCode>
        <contactPerson>
            <name>Fiona</name>
        </contactPerson>
        <contactPerson>
            <name>Fabian</name>
        </contactPerson>
        <contactPerson>
            <name>Florian<name>
        </contactPerson>
    </memberState>
    <memberState>
        <countryCode>GB</countryCode>
        <contactPerson>
            <name>Gabi</name>
        </contactPerson>
        <contactPerson>
            <name>Gert</name>
        </contactPerson>
    </memberState>
</application>

プロセスの次の手順を特定しました。

  1. countryCode -Tagsを取得し、値をコンマで分割して1つのリストに入れます
  2. リスト内の二重出現を削除します
  3. リスト内の値ごとに1つの新しいcountryCodeノードを作成します
  4. 新しいcountryCodeノードごとに、その国の連絡担当者であるすべての人を追加します

今、私はステップ1を行う方法を理解しました:

<memberState>
    <countryCodes>
        <xsl:for-each select="/application/contactPerson">
            <xsl:for-each select="tokenize(./countryCode, ',')">
                <countryCode>
                    <xsl:value-of select="."/>
                </countryCode>
            </xsl:for-each>
        </xsl:for-each>
    </countryCodes>
</memberState>

ステップ2では、を使用できますdistinct-values()

ステップ3+ステップ4では、次のソリューションを実装しました。

<xsl:for-each select="/application/contactPerson/countryCode[not(. = ../preceding-sibling::*/countryCode)]">
    <memberState>
        <countryCode>
            <xsl:value-of select="."/>
        </countryCode>
        <xsl:for-each select="/application/contactPerson[countryCode = current()]">
            <contactPerson>
                <name>
                    <xsl:value-of select="name"/>
                </name>
            </contactPerson>
        </xsl:for-each>
    </memberState>
</xsl:for-each>

しかし、どうすればすべてをまとめることができますか?私の考えは、各ステップの出力を変数に保存して次のステップで操作することでしたが、XSLTの変数が読み取り専用であるという事実に問題がありました。単一のソリューションを何らかの方法で接続して、目的の結果を得る方法はありますか?

4

1 に答える 1

8

私は単にシングルステップのfor-each-group解決策を提案します:

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

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="application">
  <xsl:copy>
    <xsl:for-each-group select="contactPerson" group-by="tokenize(countryCode, ',')">
      <memberState>
        <countryCode><xsl:value-of select="current-grouping-key()"/></countryCode>
        <xsl:apply-templates select="current-group()"/>
      </memberState>
   </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

<xsl:template match="contactPerson/countryCode"/>

</xsl:stylesheet>

もちろん、いくつかの変換ステップが可能ですがfor-each-group、XSLT 2.0が提供するようなツールでは、いくつかの変換ステップを使用する代わりに、最初にこれらを使用することを検討します。

使用したい場合distinct-valuesはもちろん可能です。ただし、文字列値を変数に格納して操作するだけで、XSLT2.0で一時ツリーが必要になる理由がわかりません。distinct-valuesしたがって、これは、2番目のステップで処理されるようにそれらを格納するためのとを使用するサンプルです(そして私は効率のためにキーを使用します):

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

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="countryCodes" select="distinct-values(application/contactPerson/countryCode/tokenize(., ','))"/>

<xsl:variable name="main-input" select="/"/>

<xsl:key name="country" match="contactPerson" use="tokenize(countryCode, ',')"/>

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

<xsl:template match="application">
  <xsl:copy>
    <xsl:for-each select="$countryCodes">
      <memberState>
        <countryCode><xsl:value-of select="."/></countryCode>
        <xsl:apply-templates select="key('country', ., $main-input)"/>
      </memberState>
   </xsl:for-each>
  </xsl:copy>
</xsl:template>

<xsl:template match="contactPerson/countryCode"/>

</xsl:stylesheet>

ただし、XSLT 2.0のサポートがあれば、私の最初の提案は簡単だと思います。

于 2013-02-12T15:11:14.713 に答える