我知道作为一种函数式语言,XSL没有传统的for循环(但是for-each)。
我正在尝试从可变数量的元素开始创建一个固定数量为(7)的表。总之,我有
<items>
<item />
<item />
<item />
</item>
如何将其转换为
<table>
<tr><item /></tr>
<tr><item /></tr>
<tr><item /></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
</table>
?使用count()
,很容易计算出我需要4个空,但是如何做到这一点?使用for循环我可以很容易地解决问题,或者修改<items>
向其中添加4个空元素,但是,对于xsl来说,我甚至不能这样做。
由于
答案 0 :(得分:3)
您正在寻找递归解决方案。该解决方案涉及编写一个模板,当传入计数小于您需要运行模板的次数时,该模板会调用自身。
IBM发布了一个很好的例子:
http://www.ibm.com/developerworks/xml/library/x-tiploop.html
您的代码可能类似于:
<xsl:template name="itemLoop">
<xsl:param name="count" select="0"/>
<xsl:if test="$count < 7">
<tr><xsl:apply-templates select="/items/item[$count]"/></tr>
<xsl:call-template name="itemLoop">
<xsl:with-param name="count" select="$count + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
答案 1 :(得分:1)
最好在可能的情况下始终避免递归。
在XSLT 2.0中,只需编写:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumRows" select="7"/>
<xsl:variable name="vDoc" select="/"/>
<xsl:template match="items">
<table>
<xsl:for-each select="1 to $pNumRows">
<tr><xsl:copy-of select="$vDoc/items/item[current()]"/></tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的XML文档时:
<items>
<item />
<item />
<item />
</items>
产生了想要的正确结果:
<table>
<tr>
<item/>
</tr>
<tr>
<item/>
</tr>
<tr>
<item/>
</tr>
<tr/>
<tr/>
<tr/>
<tr/>
</table>
很少有人知道,对于大量案例,人们也可以避免在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:param name="pNumRows" select="7"/>
<xsl:param name="vDoc" select="/"/>
<xsl:template match="items">
<table>
<xsl:for-each select=
"(document('')//node())[not(position() > $pNumRows)]">
<xsl:variable name="vPos" select="position()"/>
<tr><xsl:copy-of select="$vDoc/items/item[position()=$vPos]"/></tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
这被称为Piez的方法,人们可以阅读 here 。
记住:递归比简单迭代慢得多 - 如果列表有相当长的时间(大约1000或更多),并且不采取特殊的编程措施,它也会因堆栈溢出而崩溃。 / p>
答案 2 :(得分:0)
你应该看看这个问题:
How to count elements in categorys and compare it with other categorys in XSLT
那里讨论了同样的问题(标题没有提到这个),并且有很多关于如何解决这个问题的好答案。
答案 3 :(得分:-1)
这是一个可以满足您需求的示例表。我使用count
计算出有多少项,然后我需要创建多少空行。我使用了for_loop模板。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<table>
<xsl:for-each select="//items/item">
<tr><item/></tr>
</xsl:for-each>
<xsl:variable name="itemCount" as="xs:integer" select="count(//items/item)" />
<xsl:call-template name="for_loop">
<xsl:with-param name="i">
<xsl:value-of select="0" />
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="7-$itemCount" />
</xsl:with-param>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="for_loop">
<xsl:param name="i" />
<xsl:param name="count" />
<xsl:if test="$i < $count">
<tr></tr>
</xsl:if>
<xsl:if test="$i < $count">
<xsl:call-template name="for_loop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1" />
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count" />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>