XSL: проблема с циклированием - PullRequest
2 голосов
/ 09 марта 2011

Я знаю, что, будучи функциональным языком, в XSL нет ничего похожего на традиционный цикл for (но для каждого).

Я пытаюсь создать таблицу с фиксированным числом (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 ]

3 голосов
/ 09 марта 2011

Вы ищете рекурсивное решение. Решение заключается в написании шаблона, который вызывает сам себя, когда переданное число меньше, чем количество раз, которое вам нужно для запуска шаблона.

Хороший пример опубликован 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 голос
/ 09 марта 2011

Лучше всегда избегать рекурсии, когда это возможно .

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

Это называется методом Пьеза, о котором можно прочитать здесь .

Помните : рекурсия намного медленнее, чем простая итерация - она ​​также имеет тенденцию к аварийному завершению с переполнением стека, если список имеет значительную длину (около 1000 или более) и специальные программные меры не принимаются.

0 голосов
/ 09 марта 2011

Вот пример листа, который делает то, что вы хотите.Я выясняю, сколько элементов используется 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>
0 голосов
/ 09 марта 2011

Вы должны взглянуть на этот вопрос здесь:

Как считать элементы в категориях и сравнивать их с другими категориями в XSLT

Существует та же проблемаобсуждается там (название не упоминает об этом), и есть много хороших ответов о том, как решить эту проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...