9

Muenchian グループ化はどのように詳細に機能しますか?

データベースから生成された単純な XML ドキュメントがあります。

<CLIENTS>
    <CLIENT>
       <NAME>John</NAME>
       <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
       <LAST_USED>2012-10-03</LAST_USED>
       <AMOUNT>5000</AMOUNT>

    </CLIENT>
    <CLIENT>
       <NAME>John</NAME>
       <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
       <LAST_USED>2012-10-02</LAST_USED>
       <AMOUNT>10000</AMOUNT>
    </CLIENT>
       ...

名前ノードでグループ化したいと思います。目的の出力を次のようにするにはどうすればよいですか?

<ClIENTS>
    <CLIENT>
        <NAME>John</NAME>
        <ACCOUNT>
           <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
           <LAST_USED>2012-10-03</LAST_USED>
           <AMOUNT>5000</AMOUNT>
        </ACCOUNT>
        <ACCOUNT>
           <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
           <LAST_USED>2012-10-03</LAST_USED>
           <AMOUNT>10000</AMOUNT>
        </ACCOUNT>
       ....
</CLIENTS>
4

4 に答える 4

12

キーを定義するコードのヘルプについては、www.jenitennison.com/xslt/grouping/muenchian.xml を参照してください。

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/>

次に、テンプレートを次のように使用します

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


<xsl:template match="CLIENTS">
  <xsl:copy>
    <xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/>
  <xsl:copy>
</xsl:template>

<xsl:template match="CLIENT" mode="group">
  <xsl:copy>
    <xsl:copy-of select="NAME"/>
    <xsl:apply-templates select="key('client-by-name', NAME)"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="CLIENT">
  <ACCOUNT>
    <xsl:apply-templates select="node()[not(self::NAME)]"/>
  </ACCOUNT>
</xsl:template>

[編集] XSLT 2.0 を使用する場合は、もちろん Muenchian グループ化は必要ありません。代わりに、

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

<xsl:template match="CLIENTS">
  <xsl:copy>
    <xsl:for-each-group select="CLIENT" group-by="NAME">
      <CLIENT>
        <xsl:apply-templates select="NAME, current-group()"/>
      </CLIENT>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

<xsl:template match="CLIENT">
  <ACCOUNT>
    <xsl:apply-templates select="node() except NAME"/>
  </ACCOUNT>
</xsl:template>
于 2012-10-08T14:56:20.690 に答える
4

Jeni Tennison は、Muenchian グループ化を実行するために必要な手順を次のように説明しています。

http://www.jenitennison.com/xslt/grouping/muenchian.html

基本的に、XSLT を使用してノードにキーを割り当てます。ドキュメント内に同一のノードがある場合、このキーを繰り返すことができます。次に、XSLT は各キーを通過し、一致するキーを持つノードを出力できるようにします。

したがって、マーティンの回答では、この行は NAME ノードの内容に基づいて各クライアントのキーを作成しています (名前が複数のクライアントで同じである場合、キーも同じであることを思い出してください)。

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/>

次に、すべてのキーを調べて、それぞれの最初のインスタンスを見つけます (再びマーティンの回答を使用)。

<xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/>

次に、キーに一致するすべてのクライアントを検索して、詳細を出力できるようにする必要があります (再び、Martins)。

<xsl:apply-templates select="key('client-by-name', NAME)"/>

ここから、CLIENT の詳細を出力する別のテンプレートが必要になります。

于 2012-10-08T15:06:44.647 に答える
3

Muenchian グループ化 (@Martin の回答による) は、グループ化中に従来のグループ化戦略が持つ冗長性を排除します。

Muenchian Grouping を使用しない場合、テンプレートは通常、preceding-siblingまたはfollowing-sibling各グループの最初の候補インスタンスを決定するために使用され、次のように、グループに一致するすべてのノードを検索するために 2 番目のクエリが必要になります。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="CLIENTS">
        <CLIENTS>
            <!--Only find the 'first' instance of each client-->
            <xsl:apply-templates select="CLIENT[not(NAME = preceding-sibling::CLIENT/NAME)]" mode="client"/>
        </CLIENTS>
    </xsl:template>

    <xsl:template match="CLIENT" mode="client">
        <xsl:variable name="name" select="NAME"/>
        <CLIENT>
            <NAME>
                <xsl:value-of select="$name"/>
            </NAME>
            <ACCOUNTS>
                <!--Note that we now have to find the other matching clients *again* - this is the inefficiency that Muenchian grouping eliminates-->
                <xsl:apply-templates select="/CLIENTS/CLIENT[NAME/text()=$name]" mode="account" />
            </ACCOUNTS>
        </CLIENT>
    </xsl:template>

    <xsl:template match="CLIENT" mode="account">
        <ACCOUNT>
            <!--Copy everything else except Name, which is the grouping key -->
            <xsl:copy-of select="@* | *[not(local-name='NAME')]"/>
        </ACCOUNT>
    </xsl:template>

</xsl:stylesheet>
于 2012-10-08T15:13:51.180 に答える
1

以前のコメント (@Martin の回答の下) で、OP は XSLT 2.0 のfor-each-group要素を使用してこの問題を解決できるかどうかを尋ねました。

この XSLT 2.0 ソリューションの場合:

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

  <xsl:template match="/*">
    <CLIENTS>
      <xsl:for-each-group select="CLIENT" group-by="NAME">
        <CLIENT>
          <xsl:sequence select="NAME" />
          <xsl:for-each select="current-group()">
            <ACCOUNT>
              <xsl:sequence select="*[not(self::NAME)]" />
            </ACCOUNT>
          </xsl:for-each>
        </CLIENT>
      </xsl:for-each-group>
    </CLIENTS>
  </xsl:template>

</xsl:stylesheet>

...OP の元の XML に適用されます。

<CLIENTS>
  <CLIENT>
    <NAME>John</NAME>
    <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
    <LAST_USED>2012-10-03</LAST_USED>
    <AMOUNT>5000</AMOUNT>
  </CLIENT>
  <CLIENT>
    <NAME>John</NAME>
    <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
    <LAST_USED>2012-10-02</LAST_USED>
    <AMOUNT>10000</AMOUNT>
  </CLIENT>
</CLIENTS>

...必要な結果が生成されます。

<?xml version="1.0" encoding="utf-8"?>
<CLIENTS>
  <CLIENT>
    <NAME>John</NAME>
    <ACCOUNT>
      <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
      <LAST_USED>2012-10-03</LAST_USED>
      <AMOUNT>5000</AMOUNT>
    </ACCOUNT>
    <ACCOUNT>
      <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
      <LAST_USED>2012-10-02</LAST_USED>
      <AMOUNT>10000</AMOUNT>
    </ACCOUNT>
  </CLIENT>
</CLIENTS>

説明:

すでに正しく推測されているように、XSLT 2.0 はfor-each-group要素 (および などの関連するパートナーcurrent-group()) を導入し、Muenchian メソッドのような驚くべき/印象的でありながら混乱を招く可能性のあるグループ化方法論を排除しました。

于 2012-10-08T16:19:53.327 に答える