我可以在xslt中创建一个带计数器的循环吗?

时间:2015-07-20 05:47:42

标签: xml xslt

似乎有很多类似的问题,但没有一个对我有帮助。我想重构下面的XSLT:

<fo:table-cell xsl:use-attribute-sets="table-item-bordered">
    <fo:block>
        <xsl:value-of select="/forms//instance[@versionNumber = '1']/content//textbox[@id = $id]/text()" />
    </fo:block>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="table-item-bordered">
    <fo:block>
        <xsl:value-of select="/forms//instance[@versionNumber = '2']/content//textbox[@id = $id]/text()" />
    </fo:block>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="table-item-bordered">
    <fo:block>
        <xsl:value-of select="/forms//instance[@versionNumber = '3']/content//textbox[@id = $id]/text()" />
    </fo:block>
</fo:table-cell>

注意更改versionNumber属性。

xml源看起来像这样:

<forms>
  <instance versionNumber="3">
    <content>
      <textbox id="1">THREE</textbox>
    </content>
    <previousVersions>
      <instance versionNumber="1">
        <content>
          <textbox id="1">ONE</textbox>
        </content>
      </instance>
      <instance versionNumber="2">
        <content>
          <textbox id="1">TWO</textbox>
        </content>
      </instance>
    </previousVersions>
  </instance>
</forms>

输出应该是一个格式如下的表行:

+-----+-----+-------+
| ONE | TWO | THREE |
+-----+-----+-------+

在代码中,这看起来像这样:

<table-cell>ONE</table-cell>
<table-cell>TWO</table-cell>
<table-cell>THREE</table-cell>

这很容易,不使用循环计数器,但是我总是需要3个单元格,无论有多少个版本。如果没有versionNumber = 3的实例,则表格必须如下所示:

+-----+-----+-----+
| ONE | TWO |     |
+-----+-----+-----+

或者用XML:

<table-cell>ONE</table-cell>
<table-cell>TWO</table-cell>
<table-cell></table-cell>

4 个答案:

答案 0 :(得分:2)

没有循环但有其他模板:

<xsl:template match="instance">
  <xsl:param name="id" />

  <fo:table-cell xsl:use-attribute-sets="table-item-bordered">
    <fo:block>
      <xsl:value-of select="content//textbox[@id = $id]/text()" />
    </fo:block>
  </fo:table-cell>
</xsl:template>

<!-- in another template... -->
<xsl:apply-templates select="/forms//instance[@id = $instanceId and (@versionNumber &gt;= 1 and @versionNumber &lt;= 3)]">
  <xsl:with-param name="id" select="$id"/>
  <xsl:sort select="@versionNumber" data-type="number"/>
</xsl:apply-templates>

答案 1 :(得分:1)

  

无论有多少版本,我总是需要3个单元格。

只有三个单元格,您可以简单地对它们进行硬编码。如果您不想这样做,那么必须使用循环,例如:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="instance" match="instance" use="@versionNumber" />

<xsl:template match="/forms">
    <root>
        <xsl:call-template name="generate-cells">
            <xsl:with-param name="n" select="3" />
        </xsl:call-template>
    </root>
</xsl:template>

<xsl:template name="generate-cells">
    <xsl:param name="n"/>
    <xsl:if test="$n">
        <!-- recursive call -->
        <xsl:call-template name="generate-cells">
            <xsl:with-param name="n" select="$n - 1" />
        </xsl:call-template>
        <!-- current cell -->
        <table-cell>
            <xsl:value-of select="key('instance', $n)/content/textbox" />
        </table-cell>
    </xsl:if>   
</xsl:template>

</xsl:stylesheet>
  

在函数式语言中,我可以调用&#39; map&#39;在集合[1,2,3]和   从那里开始,有没有XSLT相当于这样做?

在XSLT 2.0中,您可以执行以下操作:

<xsl:template match="/forms">
    <xsl:variable name="forms" select="." />
    <root>
        <xsl:for-each select="('1','2','3')">
            <table-cell>
                <xsl:value-of select="key('instance', ., $forms)/content/textbox" />
            </table-cell>
        </xsl:for-each>
    </root>
</xsl:template>

甚至:

    <xsl:for-each select="1 to 3">
        <table-cell>
            <xsl:value-of select="key('instance', string(.), $forms)/content/textbox" />
        </table-cell>
    </xsl:for-each>

答案 2 :(得分:1)

来自评论:

  

我需要准确地拥有3个表格单元格,无论有多少个实例节点,即使只有1个或2个。

然后你可以使用一个小技巧并循环遍历三个不相关的节点并滥用position()

<xsl:for-each select="//*[position() &lt;= 3]">
  <xsl:variable name="i" select="position()" />

  <fo:table-cell xsl:use-attribute-sets="table-item-bordered">
    <fo:block>
      <xsl:value-of select="content/forms//instance[@versionNumber = $i]//textbox[@id = $id]/text()" />
    </fo:block>
  </fo:table-cell>
</xsl:for-each>

或者您可以使用递归,这对于XSLT来说更为惯用。它适用于任意数量的重复,但也更长:

<xsl:template name="output-table-cell">
  <xsl:param name="i" />
  <xsl:param name="max" />

  <xsl:if test="$i &lt;= $max">
    <fo:table-cell xsl:use-attribute-sets="table-item-bordered">
      <fo:block>
        <xsl:value-of select="content/forms//instance[@versionNumber = $i]//textbox[@id = $id]/text()" />
      </fo:block>
    </fo:table-cell>

    <!-- recursive step -->
    <xsl:call-template name="output-table-cell">
      <xsl:with-param name="i" select="$i + 1" />
      <xsl:with-param name="max" select="$max" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

请注意,我假设在两个片段中的其他地方都定义了$id。根据需要进行调整。

答案 3 :(得分:0)

受@ michael.hor257k的启发,我最终没有循环,而是创建了一个命名模板并调用了3次。这足以减少代码重复并使其更清晰:

<xsl:call-template name="table-cell">
    <xsl:with-param name="node" select="/forms//instance[@versionNumber = '1']/content//textbox[@id = $id]" />
</xsl:call-template>
<xsl:call-template name="table-cell">
    <xsl:with-param name="node" select="/forms//instance[@versionNumber = '2']/content//textbox[@id = $id]" />
</xsl:call-template>
<xsl:call-template name="table-cell">
    <xsl:with-param name="node" select="/forms//instance[@versionNumber = '3']/content//textbox[@id = $id]" />
</xsl:call-template>

<xsl:template name="tableCell">
    <xsl:param name="node" />
    <fo:table-cell xsl:use-attribute-sets="table-item-bordered">
        <fo:block>
            <xsl:value-of select="$node/text()" />
        </fo:block>
    </fo:table-cell>
</xsl:template>