在我的阅读中,很明显,xslt 1.0中for i .. m
循环的唯一明智的解决方法是使用递归模板。
除非有人能另外解释,否则进一步看来,由于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
属性在输入文档的元素中非常常见,语义意图正如我上面的示例所描述的。如果我要使用递归迭代器方法,我必须将其烘焙到每个模板中(这将非常不干燥;更像是非常潮湿,如“Why even try”;但我偏离了方向)
如果有一种策略来创建一个通用的
for
模板,使用它可以执行任何转换操作,那将非常壮观。如果我不使用任何递归迭代器就可以离开,如果在xslt 1.0中隐藏了一些用于此目的的函数的精华,那将是同样壮观的。无论如何,我怎样才能做到这一点?我需要用湿的方法,还是有更好的方法?
最佳答案
递归没有错。在这种情况下,让它足够通用应该很容易。
这里有一个例子。除了输出与count
相同的子元素外,我还将name
元素更改为new_elem
(只是为了显示额外的转换)。
XSLT 1
<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>