2

for i .. m私の読書では、XSLT 1.0 のループに対する唯一の適切な回避策は、再帰的なテンプレートを使用することであることが明らかになりました。

誰かが別の方法で説明できない限り、XSLT 処理の制約を考えると、このアプローチは一般的に再利用可能ではないようです。

とにかく、次の入力スニペット (この場合はコンテキスト ノード) が与えられた場合:

<items count="3">
    <item>
        <name>Name</name>
        <description>Description</description>
    </item>
</items>

属性<item>に基づいて子を複製するための再利用可能な戦略はありますか? countここで期待される出力は単純に

<item>
    <name>Name</name>
    <description>Description</description>
</item>
<item>
    <name>Name</name>
    <description>Description</description>
</item>
<item>
    <name>Name</name>
    <description>Description</description>
</item>

ノードでさらに変換を実行するつもり<item>ですが、関係ないと思います。

再利用性は私にとって重要なポイントです。count属性が入力ドキュメントの要素間で非常に一般的であり、セマンティックな意図が上記の例で説明したとおりであるという単純な理由からです。

再帰イテレータ アプローチを使用する場合は、それをすべてのテンプレートに焼き付ける必要があります (これは DRY ではなく、「なぜ試してみる必要があるか」のように WET に似ていますが、脱線します) 。

変換操作を実行できる汎用テンプレートを作成する戦略あればfor、それは素晴らしいことです。再帰イテレータを使用せずに済む場合、この目的のための関数の宝石が XSLT 1.0 に隠されている場合、それは同様に素晴らしいことです。

とにかく、どうすればこれを達成できますか?WET アプローチに頼る必要がありますか、それとももっと良い方法がありますか?

4

2 に答える 2

3

再帰に問題はありません。この状況に十分な汎用性を持たせることは非常に簡単なはずです。

例を次に示します。と同じ数の子要素を出力することに加えて、要素を次のようcountに変更します(追加の変換を示すためだけに)...namenew_elem

XSLT1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <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="*[@count]">
        <xsl:apply-templates select="*" mode="dupe">
            <xsl:with-param name="count" select="@count"/>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="*" mode="dupe">
        <xsl:param name="count"/>
        <xsl:apply-templates select="."/>
        <xsl:if test="$count > 1">
            <xsl:apply-templates mode="dupe" select=".">
                <xsl:with-param name="count" select="$count - 1"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>

    <xsl:template match="name">
        <new_elem><xsl:value-of select="."/></new_elem>
    </xsl:template>

</xsl:stylesheet>

出力(質問からの入力を使用)

<item>
   <new_elem>Name</new_elem>
   <description>Description</description>
</item>
<item>
   <new_elem>Name</new_elem>
   <description>Description</description>
</item>
<item>
   <new_elem>Name</new_elem>
   <description>Description</description>
</item>

XSLT 2.0 を使用できる場合は、質問の冒頭で述べたように繰り返すことができます。

XSLT 2.0 (上記と同じ出力を生成します。 の子要素が複数ある場合は少し異なります*[@count])

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <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="*[@count]">
        <xsl:variable name="curr" select="."/>
        <xsl:for-each select="1 to @count">
            <xsl:apply-templates select="$curr/*"/>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="name">
        <new_elem><xsl:value-of select="."/></new_elem>
    </xsl:template>

</xsl:stylesheet>
于 2013-04-23T04:35:29.157 に答える
2

任意の変換操作を実行できるジェネリック for テンプレートを作成する戦略があれば、それは素晴らしいことです。再帰イテレータを使用せずに済む場合、この目的のための関数の宝石が XSLT 1.0 に隠されている場合、それは同様に素晴らしいことです。

とにかく、どうすればこれを達成できますか?WET アプローチに頼る必要がありますか、それとももっと良い方法がありますか?

必要な DRY ネスは、FXSLのテンプレート/関数の簡単なアプリケーションiterです。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://fxsl.sf.net/" xmlns:myRepeat="f:myRepeat"
 exclude-result-prefixes="xsl f myRepeat">
 <xsl:import href="iter.xsl"/>

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

  <myRepeat:myRepeat/>
  <xsl:variable name="vFunRepeat" select="document('')/*/myRepeat:*[1]"/>

  <xsl:variable name="vAdditive" select="/*/*[1]"/>

  <xsl:template match="/*">
        <xsl:call-template name="iter">
          <xsl:with-param name="pTimes" select="@count"/>
          <xsl:with-param name="pFun" select="$vFunRepeat"/>
          <xsl:with-param name="pX" select="/.."/>
        </xsl:call-template>
  </xsl:template>

  <xsl:template match="myRepeat:*" mode="f:FXSL">
    <xsl:param name="arg1"/>

    <xsl:copy-of select="$arg1 | $vAdditive"/>
  </xsl:template>
</xsl:stylesheet>

この変換が提供された XML ドキュメントに適用されると、次のようになります。

<items count="3">
    <item>
        <name>Name</name>
        <description>Description</description>
    </item>
</items>

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

<item>
   <name>Name</name>
   <description>Description</description>
</item>
<item>
   <name>Name</name>
   <description>Description</description>
</item>
<item>
   <name>Name</name>
   <description>Description</description>
</item>

注意してください:

  1. 再帰的なテンプレートを書く必要はまったくありません。

  2. テンプレートは、他のほとんどの FXSLのiterテンプレートと同様に、非常に一般的で強力であり、プログラマーが何度も再帰を記述してデバッグする必要がなくなります。

  3. FXSL は、必要な DRY 性を提供し、より抽象的な思考を刺激します。つまり、より強力な構成要素 (関数、折り畳み、反復など) です。

  4. この問題は、XSLT 2.0 で自明な解決策を持っている ( <xsl:for-each select="1 to @count">) ためですが、他にも多くの問題があり、XSLT 2.0 での解決策はそれほど単純ではありません。FXSL は、折り畳み、マップ、スキャン、zip など、最も一般的で強力な高次関数の汎用的で強力な実装を使用して、このような「難しい」問題を解決するのに役立ちます。


Ⅱ.Piez 法を使用する( の値に既知の上限がある場合@count)

<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:variable name="vStyle" select="document('')"/>
 <xsl:variable name="vNodes" select=
   "$vStyle//node()|$vStyle//@* | $vStyle//namespace::*"/>

 <xsl:variable name="vAdditive" select="/*/*[1]"/>

 <xsl:template match="/*">
     <xsl:for-each select="$vNodes[not(position() > current()/@count)]">
       <xsl:copy-of select="$vAdditive"/>
     </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

この変換を同じ XML ドキュメント (上記) に適用すると、同じ正しい結果が生成されます。

<item>
   <name>Name</name>
   <description>Description</description>
</item>
<item>
   <name>Name</name>
   <description>Description</description>
</item>
<item>
   <name>Name</name>
   <description>Description</description>
</item>

注意してください:

Piez 法を使用できる場合、再帰は完全に回避されます。

免責事項

私は 11 ~ 12 年前に FXSL を開発できてうれしかったです。それ以来、免責事項を提供する必要があることを知らずに、2 つの会議論文を含め、何千回も言及してきました:)

于 2013-04-23T04:44:10.730 に答える