如何将<xsl:foreach>拆分为多个部分?</xsl:foreach>

时间:2009-10-08 21:25:35

标签: xml xslt cdata

我有一个元素列表,我想分成3个单独的列表。最终结果将是这样的:

<ul>
    <li>element</li>
    <li>element</li>
</ul>
<ul>
    <li>element</li>
    <li>element</li>
</ul>
<ul>
    <li>element</li>
    <li>element</li>
</ul>

我的XSLT是这样的,但它不起作用,因为我无法插入</ul>,并且我无法插入小于号(<)。

<ul>
    <xsl:for-each select="$myroot/item">
        <li></li>

        <xsl:if test="position() mod $maxItemsPerColumn = 0">
            <!-- I want to close my ul, and start a new one here, but it doesn't work! -->
        </xsl:if>
    </xsl:for-each>
</ul>

有什么想法吗?提前谢谢!

4 个答案:

答案 0 :(得分:9)

你不需要像递归一样做任何奇特的事情。好主,甚至不打算使用CDATA。

你只需要像XSLT一样思考并问:“我想将哪个输入元素转换为输出元素?”

假设每个ul应该包含N item s,您希望将每个第N个输入item从第一个开始转换为ul:< / p>

<xsl:variable name="n" select="number(4)"/>

<xsl:template match="/">
  <output>
    <xsl:apply-templates select="/root/item[position() mod $n = 1]"/>
  </output>
</xsl:template>

这些item元素中的每一个都变成ul,其中包含项目及其每个N-1兄弟姐妹:

<xsl:template match="item">
  <ul>
    <xsl:for-each select=". | following-sibling::item[position() &lt; $n]">
      <li>
        <xsl:value-of select="."/>
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>

假设输入文档如下:

<root>
  <item>1</item>
  <item>2</item>
  <item>3</item>
  <item>4</item>
  <item>5</item>
  <item>6</item>
  <item>7</item>
  <item>8</item>
  <item>9</item>
</root>

...如果$n设置为4,则获得此输出:

<output>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
  </ul>
  <ul>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
  </ul>
  <ul>
    <li>9</li>
  </ul>
</output>

答案 1 :(得分:1)

您可以使用递归解决方案:

<xsl:call-template name="group">
  <xsl:with-param name="items" select="$myroot/item" />
</xsl:call-template>

<xsl:template name="group">
  <xsl:param name="items" />
  <xsl:if test="count($items) > 0">
    <ul>
      <xsl:for-each select="$items[position() &lt;= 3]">
        <li>...</li>
      </xsl:for-each>
    </ul>
    <xsl:call-template name="group">
      <xsl:with-param name="items" select="$items[position() > 3]" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

这样做是为group模板调用整个项目列表。 group模板在<ul> ... </ul>标记内写出列表的前三个元素(如果没有三个,则更少)。然后它再次调用自己,其余的项目列表省略前三个。当列表为空时,group模板不执行任何操作。

XSLT是一种非常实用的语言,遵循这些规则(即不使用disable-output-escaping)将在您将来需要再次修改模板时避免痛苦和痛苦。

答案 2 :(得分:0)

你可以尝试这样的事情(没有经过测试,但你已经有了想法)

(...)
<xsl:call-template name="recursive">
 <xsl:with-param name="root" select="$myroot"/>
 <xsl:with-param name="index" select="number(1)"/>
</xsl:call-template>
(...)


<xsl:template name="recursive">
<xsl:param name="root"/>
<xsl:param name="index"/>
<ul>
<li><xsl:value-of select="$root/item[$index]"/></li>
<li><xsl:value-of select="$root/item[$index +1 ]"/></li>
</ul>
<xsl:if test="$root/item[$index+2]">
<xsl:call-template name="recursive">
 <xsl:with-param name="root" select="$root"/>
 <xsl:with-param name="index" select="$index+2"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

答案 3 :(得分:-1)

你可以做到

<xsl:text disable-output-escaping="yes"><![CDATA[</ul>]]></xsl:text>

<xsl:text disable-output-escaping="yes"><![CDATA[<ul>]]></xsl:text>

所以在你的情况下:

<ul>
    <xsl:for-each select="$myroot/item">
        <li></li>

        <xsl:if test="position() mod $maxItemsPerColumn = 0">
            <xsl:text disable-output-escaping="yes"><![CDATA[</ul>]]></xsl:text>
            <xsl:text disable-output-escaping="yes"><![CDATA[<ul>]]></xsl:text>                
        </xsl:if>
    </xsl:for-each>
</ul>

更新我在阅读了Pierre和Greg的回答之后删除了这个,但我决定保留它,因为它确实回答了你的问题,它可能对某人有用,对某个地方。

更新2:是的,我理解为什么这可能是可怕的,并激发了同伴中的Nazgûl般的恐惧,是的,我试图自己对此投票,但我认为这个答案可能有助于将来的某个人。