0

フィード内の各製品には、次のようなカテゴリ ノードがあります。

<categories>
  <category>För henne &gt; Ansikte</category>
  <category>För henne &gt; Pumps &amp; klackskor</category>
  <category>För henne &gt; Stövlar &amp; stövletter</category>
  <category>För honom &gt; Badkläder</category>
  ...
</categories>

&gt;親カテゴリと子カテゴリを分ける場所。

私がやりたいのは、次のようなカテゴリ ツリーを持つことです。

<categories>
  <category name="För henne">
    <categories>
      <category name="Ansikte"/>
      <category name="Pumps &amp; klackskor"/>
      <category name="Stövlar &amp; stövletter"/>
    </categories>
  </category>
  <category name="För honom">
    <categories>
      <category name="Badkläder"/>
    </categories>
  </category>
</categories>

xslでこれを達成するにはどうすればよいですか?

4

2 に答える 2

1

これは、代替の XSLT 2.0 スタイルシートです。これは、最も単純でありながら汎用的なソリューションであると私は信じています。

この変身…

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' &gt; '" />

<xsl:template match="categories" name="cat-grouper">
  <xsl:param name="cat-strs" as="xs:string*" select="category" />
  <categories>
    <xsl:for-each-group select="$cat-strs[contains(.,$sep)]" group-by="substring-before(.,$sep)">
      <category name="{current-grouping-key()}">
        <xsl:call-template name="cat-grouper">
          <xsl:with-param name="cat-strs" select="for $s in current-group() return substring-after($s,$sep)" />
        </xsl:call-template>
      </category>
    </xsl:for-each-group>
    <xsl:for-each-group select="$cat-strs[not(contains(.,$sep))]" group-by=".">
      <category name="{current-grouping-key()}" />
    </xsl:for-each-group>
  </categories> 
</xsl:template>

</xsl:stylesheet>

...次の入力に適用されます(OPのサンプル入力から適合させて、<category>För honom > Badkläder</category>入力での2回の出現に関連する彼のコメントに対処します)...

<categories>
  <category>För henne &gt; Ansikte</category>
  <category>För henne &gt; Pumps &amp; klackskor</category>
  <category>För henne &gt; Stövlar &amp; stövletter</category>
  <category>För honom &gt; Badkläder</category>
  <category>För honom &gt; Badkläder</category>
</categories>

...収量...

<categories>
   <category name="För henne">
      <categories>
         <category name="Ansikte"/>
         <category name="Pumps &amp; klackskor"/>
         <category name="Stövlar &amp; stövletter"/>
      </categories>
   </category>
   <category name="För honom">
      <categories>
         <category name="Badkläder"/>
      </categories>
   </category>
</categories>

ノート

唯一無二のテンプレートに二重の目的を持たせることで、ある程度の単純さが得られます。これは、ルート要素に一致するテンプレートであるcategoriesと同時に、カテゴリ文字列のリストを処理するための名前付きテンプレートでもあります。

もう一つ

別の代替ソリューションを次に示します。私が提供した最初のものを好む場合は、これを見てみる価値があります. 同様に tokenize() を使用すると、これは Dimitre のソリューションの微調整に過ぎないと考えるかもしれません。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="sep" select="' &gt; '" />

<xsl:template match="categories" name="cat-grouper">
  <xsl:param name="cat-strs" as="xs:string*" select="category" />
  <categories>
    <xsl:for-each-group select="$cat-strs" group-by="tokenize(.,$sep)[1]">
      <category name="{current-grouping-key()}">
        <xsl:variable name="subcats" select="
          for $s in current-group()[contains(.,$sep)]
            return substring-after($s,$sep)" />
        <xsl:if test="exists($subcats)">
          <xsl:call-template name="cat-grouper">
            <xsl:with-param name="cat-strs" select="$subcats" />
          </xsl:call-template>
        </xsl:if>  
      </category>
    </xsl:for-each-group>
  </categories> 
</xsl:template>

</xsl:stylesheet>

補遺

Dimitre の入力ドキュメントに最初のソリューションを適用した結果の出力を次に示します。これが彼の変換規則の正しい解釈であるかどうか、OP にアドバイスを求めています。これは私の理解ですが、もしかしたら私の認識が間違っているのでしょうか?

<categories>
   <category name="A">
      <categories>
         <category name="AB1">
            <categories>
               <category name="AB1C1"/>
            </categories>
         </category>
         <category name="AB2">
            <categories>
               <category name="AB2C1">
                  <categories>
                     <category name="AB2C1D1"/>
                     <category name="AB2C3"/>
                  </categories>
               </category>
               <category name="AB2C1"/>
            </categories>
         </category>
      </categories>
   </category>
   <category name="F">
      <categories>
         <category name="XY1">
            <categories>
               <category name="XY1Z1"/>
            </categories>
         </category>
         <category name="XY2">
            <categories>
               <category name="XY2Z1">
                  <categories>
                     <category name="XY2Z1T1"/>
                     <category name="XY2Z3"/>
                  </categories>
               </category>
               <category name="XY2Z1"/>
            </categories>
         </category>
      </categories>
   </category>
</categories>
于 2012-10-14T11:18:42.890 に答える
0

I. XSLT 1.0 ソリューション:

この変換:

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

 <xsl:key name="kCategory" match="category"
  use="substring-before(., ' >')"/>

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

 <xsl:template match=
  "category
    [generate-id()
    =
     generate-id(key('kCategory', substring-before(., ' >'))[1])
    ]">
  <category name="{substring-before(., ' >')}">
    <categories>
      <xsl:apply-templates mode="inGroup"
        select="key('kCategory', substring-before(., ' >'))"/>
    </categories>
  </category>
 </xsl:template>

 <xsl:template match="category" mode="inGroup">
   <category name="{substring-after(., '> ')}"/>
 </xsl:template>
 <xsl:template match="category"/>
</xsl:stylesheet>

提供された XML ドキュメントに適用した場合:

<categories>
  <category>För henne > Ansikte</category>
  <category>För henne > Pumps &amp; klackskor</category>
  <category>För henne > Stövlar &amp; stövletter</category>
  <category>För honom > Badkläder</category>
  ...
</categories>

必要な正しい結果が生成されます。

<categories>
   <category name="För henne">
      <categories>
         <category name="Ansikte"/>
         <category name="Pumps &amp; klackskor"/>
         <category name="Stövlar &amp; stövletter"/>
      </categories>
   </category>
   <category name="För honom">
      <categories>
         <category name="Badkläder"/>
      </categories>
   </category>
  ...
</categories>

説明:

  1. Muenchian グループ化手法の適切な使用。

  2. アイデンティティ ルールの適切な使用とオーバーライド。

  3. キーとモードの適切な使用。


Ⅱ.一般的な XSLT 2.0 ソリューション:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" exclude-result-prefixes="my xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:sequence select="my:grouping(*)"/>
 </xsl:template>

 <xsl:function name="my:grouping">
  <xsl:param name="pStrings" as="xs:string*"/>

  <xsl:if test="$pStrings[1]">
      <categories>
          <xsl:for-each-group select="$pStrings"
            group-by="tokenize(., ' > ')[1]">

            <category name="{current-grouping-key()}">
             <xsl:variable name="vnewStrings" as="xs:string*">
                 <xsl:for-each select="current-group()">
                   <xsl:sequence select="substring-after(., ' > ')"/>
                 </xsl:for-each>
             </xsl:variable>
             <xsl:sequence select="my:grouping($vnewStrings)"/>
            </category>
          </xsl:for-each-group>
      </categories>
  </xsl:if>
 </xsl:function>
</xsl:stylesheet>

この変換が次の XML ドキュメントに適用される場合:

<categories>
  <category>A > AB1 > AB1C1</category>
  <category>A > AB2 > AB2C1 > AB2C1D1</category>
  <category>A > AB2 > AB2C1</category>
  <category>A > AB2 > AB2C1 > AB2C3</category>
  <category>F > XY1 > XY1Z1</category>
  <category>F > XY2 > XY2Z1 > XY2Z1T1</category>
  <category>F > XY2 > XY2Z1</category>
  <category>F > XY2 > XY2Z1 > XY2Z3</category>
</categories>

必要な正しい結果が生成されます。

<categories>
   <category name="A">
      <categories>
         <category name="AB1">
            <categories>
               <category name="AB1C1"/>
            </categories>
         </category>
         <category name="AB2">
            <categories>
               <category name="AB2C1">
                  <categories>
                     <category name="AB2C1D1"/>
                     <category name="AB2C3"/>
                  </categories>
               </category>
            </categories>
         </category>
      </categories>
   </category>
   <category name="F">
      <categories>
         <category name="XY1">
            <categories>
               <category name="XY1Z1"/>
            </categories>
         </category>
         <category name="XY2">
            <categories>
               <category name="XY2Z1">
                  <categories>
                     <category name="XY2Z1T1"/>
                     <category name="XY2Z3"/>
                  </categories>
               </category>
            </categories>
         </category>
      </categories>
   </category>
</categories>

説明:

  1. の使用<xsl:for-each-group>

  2. 標準 XSLT 関数の使用:current-group()およびcurrent-grouping-key().

  3. 停止条件付きの再帰: パラメーターが空です。

于 2012-10-13T21:22:05.037 に答える