これははるかに単純で(明示的な条件付き命令、ユーザー定義関数、モードなし)、より短く、機能する変換です。
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:param name="pVars" as="element()*"/>
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:with-param name="pVars" select="$pVars"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="for">
<xsl:param name="pVars" as="element()*"/>
<xsl:variable name="vCurrentFor" select="."/>
<xsl:for-each select="@from to @to">
<xsl:variable name="vnewVars">
<xsl:sequence select="$pVars"/>
<var name="{$vCurrentFor/@var}" value="{current()}"/>
</xsl:variable>
<xsl:apply-templates select="$vCurrentFor/node()">
<xsl:with-param name="pVars" select="$vnewVars/*"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()[contains(., '$(')]">
<xsl:param name="pVars" as="element()*"/>
<xsl:analyze-string select="."
regex="\$\((.+?)\)">
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
<xsl:matching-substring>
<xsl:variable name="vName" select="regex-group(1)"/>
<xsl:variable name="vReplacement" select=
"$pVars[@name eq $vName][last()]/@value"/>
<xsl:sequence select="string($vReplacement)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
この変換が提供されたXMLドキュメントに適用される場合:
<Doc>
<stuff>
<for var="i" from="1" to="2">
<item>$(i)</item>
<for var="j" from="2" to="4">
<innerItem>$(j)</innerItem>
</for>
</for>
</stuff>
</Doc>
必要な正しい結果が生成されます。
<Doc>
<stuff>
<item>1</item>
<innerItem>2</innerItem>
<innerItem>3</innerItem>
<innerItem>4</innerItem>
<item>2</item>
<innerItem>2</innerItem>
<innerItem>3</innerItem>
<innerItem>4</innerItem>
</stuff>
</Doc>
はるかに複雑な処理を実行することが可能です:
異なるレベルの変数を一緒に使用する:
<Doc>
<stuff>
<for var="x" from="1" to="2">
<item>$(x)</item>
<for var="y" from="2" to="4">
<innerItem>$(x).$(y)</innerItem>
</for>
</for>
</stuff>
</Doc>
このドキュメントの結果は次のとおりです。
<Doc>
<stuff>
<item>1</item>
<innerItem>1.2</innerItem>
<innerItem>1.3</innerItem>
<innerItem>1.4</innerItem>
<item>2</item>
<innerItem>2.2</innerItem>
<innerItem>2.3</innerItem>
<innerItem>2.4</innerItem>
</stuff>
</Doc>
またはこのXMLドキュメントで:
<Doc>
<stuff>
<for var="x" from="1" to="2">
<item>
<value>$(x)</value>
<for var="y" from="2" to="4">
<innerItem>
<value>$(x).$(y)</value>
<for var="z" from="3" to="5">
<inner-most-Item>$(x).$(y).$(z)</inner-most-Item>
</for>
</innerItem>
</for>
</item>
</for>
</stuff>
</Doc>
結果は:
<Doc>
<stuff>
<item>
<value>1</value>
<innerItem>
<value>1.2</value>
<inner-most-Item>1.2.3</inner-most-Item>
<inner-most-Item>1.2.4</inner-most-Item>
<inner-most-Item>1.2.5</inner-most-Item>
</innerItem>
<innerItem>
<value>1.3</value>
<inner-most-Item>1.3.3</inner-most-Item>
<inner-most-Item>1.3.4</inner-most-Item>
<inner-most-Item>1.3.5</inner-most-Item>
</innerItem>
<innerItem>
<value>1.4</value>
<inner-most-Item>1.4.3</inner-most-Item>
<inner-most-Item>1.4.4</inner-most-Item>
<inner-most-Item>1.4.5</inner-most-Item>
</innerItem>
</item>
<item>
<value>2</value>
<innerItem>
<value>2.2</value>
<inner-most-Item>2.2.3</inner-most-Item>
<inner-most-Item>2.2.4</inner-most-Item>
<inner-most-Item>2.2.5</inner-most-Item>
</innerItem>
<innerItem>
<value>2.3</value>
<inner-most-Item>2.3.3</inner-most-Item>
<inner-most-Item>2.3.4</inner-most-Item>
<inner-most-Item>2.3.5</inner-most-Item>
</innerItem>
<innerItem>
<value>2.4</value>
<inner-most-Item>2.4.3</inner-most-Item>
<inner-most-Item>2.4.4</inner-most-Item>
<inner-most-Item>2.4.5</inner-most-Item>
</innerItem>
</item>
</stuff>
</Doc>
ここでやめますが、この言語の優れたデザインを考えると、可能性は無限です。
更新:OPは尋ねました:
「属性内の拡張も許可する方法はありますか?例:<inner-most-Item id="$(i)">
」
はい、これは非常に簡単です。属性に一致する新しいテンプレートを追加し、コードをリファクタリングするだけです。
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:param name="pVars" as="element()*"/>
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:with-param name="pVars" select="$pVars"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="for">
<xsl:param name="pVars" as="element()*"/>
<xsl:variable name="vCurrentFor" select="."/>
<xsl:for-each select="@from to @to">
<xsl:variable name="vnewVars">
<xsl:sequence select="$pVars"/>
<var name="{$vCurrentFor/@var}" value="{current()}"/>
</xsl:variable>
<xsl:apply-templates select="$vCurrentFor/node()">
<xsl:with-param name="pVars" select="$vnewVars/*"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()[contains(., '$(')]">
<xsl:param name="pVars" as="element()*"/>
<xsl:value-of select="my:evalText($pVars, .)"/>
</xsl:template>
<xsl:template match="@*[contains(., '$(')]">
<xsl:param name="pVars" as="element()*"/>
<xsl:attribute name="{name()}">
<xsl:value-of select="my:evalText($pVars, .)"/>
</xsl:attribute>
</xsl:template>
<xsl:function name="my:evalText">
<xsl:param name="pVars" as="element()*"/>
<xsl:param name="pText"/>
<xsl:analyze-string select="$pText"
regex="\$\((.+?)\)">
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
<xsl:matching-substring>
<xsl:variable name="vName" select="regex-group(1)"/>
<xsl:variable name="vReplacement" select=
"$pVars[@name eq $vName][last()]/@value"/>
<xsl:value-of select="string($vReplacement)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
</xsl:stylesheet>
現在、この変換は次のXMLドキュメントに適用されます。
<Doc>
<stuff>
<for var="i" from="1" to="2">
<item name="X$(i)">$(i)</item>
<for var="j" from="2" to="4">
<innerItem name="X$(i).$(j)">$(j)</innerItem>
</for>
</for>
</stuff>
</Doc>
必要な正しい結果が生成されます。
<Doc>
<stuff>
<item name="X1">1</item>
<innerItem name="X1.2">2</innerItem>
<innerItem name="X1.3">3</innerItem>
<innerItem name="X1.4">4</innerItem>
<item name="X2">2</item>
<innerItem name="X2.2">2</innerItem>
<innerItem name="X2.3">3</innerItem>
<innerItem name="X2.4">4</innerItem>
</stuff>
</Doc>