【问题标题】:How can I split an <xsl:foreach> into multiple parts?如何将 <xsl:foreach> 拆分为多个部分?
【发布时间】:2010-12-05 03:57:02
【问题描述】:

我有一个元素列表,我想将其拆分为 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 是这样的,但它不起作用,因为我无法插入 &lt;/ul&gt;,也无法插入小于号 (&lt;)。

<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>

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

【问题讨论】:

  • 它可能不允许您在循环内部关闭 UL,因为 UL 在循环外部开始。尝试将其全部移动到 for-each 中

标签: xml xslt cdata


【解决方案1】:

你不需要做任何像递归这样的花哨的事情。天哪,甚至不要考虑使用 CDATA。

您只需像 XSLT 一样思考并问:“我想将什么输入元素转换为输出元素?”

假设每个ul 应该包含N 个items,您希望将每个输入item(从第一个开始)转换为ul

<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>

【讨论】:

  • @Robert Rossney:不错,干净简单。 +1
  • 这实际上很简单,但是您必须先摄取一定数量的 XSLT,然后它才会有意义。
【解决方案2】:

您可以使用递归解决方案来做到这一点:

<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 模板在&lt;ul&gt; ... &lt;/ul&gt; 标签内写出列表的前三个元素(如果没有三个元素则更少)。然后它再次调用自己,其余项目列表省略了前三个。当列表为空时,group 模板不执行任何操作。

XSLT 是一种功能性很强的语言,遵循规则(即不为此使用disable-output-escaping)将使您在将来需要再次修改模板时免于痛苦和痛苦。

【讨论】:

    【解决方案3】:

    你可以尝试这样的事情(未经测试,但你有这个想法)

    (...)
    <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>
    

    【讨论】:

      【解决方案4】:

      你可以的

      <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 的恐惧,是的,我自己试图对此投反对票,但我认为这个答案可能有助于未来的某个人。

      【讨论】:

      • 这……太可怕了。说真的,在 XSLT 中使用 either CDATA 或 disable-output-escaping 通常是在警告您正在犯某种概念上的错误。使用两者?
      • 我想你的意思是说“这个答案将来可能对某人有害”;-)
      • 至少我们知道可以这样做,即使它可能是最好的方法。
      猜你喜欢
      • 2011-05-30
      • 1970-01-01
      • 2012-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      相关资源
      最近更新 更多