1

別の構造に転送したい XML ドキュメントがあります。ドキュメントの例は次のようになります。

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

ここでやりたいことは、要素を国別にグループ化することです。つまり、結果は次のようになります。

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

すべての国を選択するために XPath の for-each パターンを使用していますが、想定どおりの動作をしません。私のパターンは次のようになります。

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

エラーは、コンパイルされない XPath 式のどこかにある可能性があります。私はそれを次のように変更しました

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

私はすでに自分の木の正しい位置にいると思うからです。このソリューションはコンパイルされますが、「先行兄弟」を使用して意図したように国の一意のリストは得られませんが、代わりに完全なリストが得られます。

私の問題の解決策が必要であることに加えて、ここで実際に何が起こっているのかを理解するための助けに特に感謝します.

  1. 2 番目のソリューションのように相対パスを指定することは可能ですか、それとも毎回完全なパスを指定する必要がありますか?
  2. 国コードの一意のリストを作成するための正しい軌道に乗っていますか、それとも一般的に別の方法で実装されますか?
  3. 私が探しているソリューションは実際に XSLT で実装できますか?

大変お世話になりました。

4

3 に答える 3

2

Muenchian グループ化を使用した XSLT 1.0 ソリューションを次に示します。

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

<xsl:output indent="yes"/>

<xsl:key name="country" match="contactPerson" use="countryCode"/>

<xsl:template match="application">
  <xsl:copy>
    <xsl:apply-templates select="contactPerson[generate-id() = generate-id(key('country', countryCode)[1])]" mode="group"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="contactPerson" mode="group">
  <memberState>
    <xsl:copy-of select="countryCode"/>
    <xsl:apply-templates select="key('country', countryCode)"/>
  </memberState>
</xsl:template>

<xsl:template match="contactPerson">
  <xsl:copy>
    <xsl:copy-of select="*[not(self::countryCode)]"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2013-01-09T12:37:59.167 に答える
2

このkeyタグを使用して、次のように countryCode で各要素にアクセスできます。

<xsl:key name="groups" match="/application/contactPerson" use="countryCode" />

次に、そのキーの内容を反復できます。XSL キーのウォーク/ループ: どのように? を参照してください。

したがって、XSLT 1.0 に固執している場合は、そこから始めるのがよいでしょう。XSLT 2.0 を使用できる場合は、 を参照してくださいgroup-by

于 2013-01-09T12:15:05.643 に答える
1

XPath式はほぼ正しかった:

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

それが機能しなかった理由は、countryCodeに兄弟がいないためです。以前のcountryCodesを見つけるには、1つ上のレベルに移動してから、下に戻る必要があります。

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

私はそれが明確なcountryCodesをうまく反復するはずだと信じています。

テンプレートに対するこの変更は機能するはずです。各国のすべての人々を反復処理するために、別のループが必要でした。

<xsl:template match="/">
    <application>
      <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>
    </application>
  </xsl:template>
于 2013-01-09T12:50:24.963 に答える