这是一个更简单(没有明确的条件指令,没有用户定义的函数,没有模式)和更短的工作转换:
<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>