XSL:骑行有问题

时间:2011-03-09 11:10:54

标签: xslt

我知道作为一种函数式语言,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来说,我甚至不能这样做。

由于

4 个答案:

答案 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 &lt; 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 &lt; $count">
            <tr></tr>
        </xsl:if>
        <xsl:if test="$i &lt; $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>