XSLT Как сделать классический цикл x-y?

Мне нужно сделать классический цикл от i=0 до N, как это можно сделать в xstl 1.0?

Благодарю.

<xsl:for-each select="¿¿¿$i=0..5???">
    <fo:block>
        <xsl:value-of select="$i"/>
    </fo:block>
</xsl:for-each>

Чтобы привести пример, у меня есть

<foo>
    <bar>Hey!</bar>
</foo>

И хочу вывод

Hey!
Hey!

3 ответа

Решение

XSLT - это функциональный язык программирования, и поэтому он сильно отличается от любых процедурных языков, которые вы уже знаете.

Хотя for В XSLT возможны циклы, они не используют присущие XSLT сильные стороны (и функциональное программирование в целом).

for Циклы обычно неправильно используются для решения проблем, которые лучше всего решать с помощью функционального подхода (то есть сопоставления шаблонов). Другими словами, цикл на самом деле не является "классическим" в XSLT.

Таким образом, вам, возможно, придется отступить назад, определить проблему, с которой вы столкнулись, вместо обсуждения вашего решения. Тогда сообщество XSLT сможет предложить решение, которое будет более функциональным по своей природе. Возможно, вы стали жертвой проблемы XY.


В настоящее время XSLT по своей сути хорош в рекурсии. Часто проблемы, которые решаются с помощью циклов в процедурных языках, решаются с помощью рекурсивных шаблонов в XSLT.

<xsl:template name="recursive-template">
   <xsl:param name="var" select="5"/>
   <xsl:choose>
     <xsl:when test="$var > 0">
       <xsl:value-of select="$var"/>
       <xsl:call-template name="recursive-template">
         <xsl:with-param name="var" select="$var - 1"/>
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise/>
   </xsl:choose>
</xsl:template>

Подводя итог, я предлагаю вам взглянуть на "классическую" рекурсию вместо "классической" for петли. Более подробную информацию об этой теме вы найдете в статье IBM здесь.


РЕДАКТИРОВАТЬ как ответ на ваш отредактированный вопрос. Если ваша проблема действительно сводится к выводу текстового контента дважды:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="text"/>

   <xsl:template match="/foo">
      <xsl:apply-templates select="bar"/>
      <xsl:apply-templates select="bar"/>
   </xsl:template>

</xsl:stylesheet>

Это, конечно, невозможно для динамического числа итераций.

XSLT 2.0 имеет <xsl:for-each select="0 to 5"> но в XSLT 1.0 вы можете только for-each через наборы узлов, а не последовательности атомарных значений. Самый простой способ, который я нашел, чтобы обойти это, состоит в том, чтобы использовать какое-то достаточно универсальное выражение селектора, которое будет соответствовать по крайней мере столько узлов, сколько вы хотите итераций, например

<xsl:for-each select="/descendant::node()[position() &lt; 7]">
    <fo:block>
        <xsl:value-of select="position() - 1"/>
    </fo:block>
</xsl:for-each>

или если вы не обязательно знаете, что во входном документе будет хотя бы 6 узлов, вы можете использовать document('') обрабатывать саму таблицу стилей как другой входной документ.

<xsl:for-each select="document('')/descendant::node()[position() &lt; 7]">

В обоих случаях for-each изменит узел контекста, поэтому вам нужно сохранить внешний контекст в переменной, если вам нужен доступ к нему внутри for-each тело

<xsl:variable name="dot" select="." />

Используйте именованный шаблон с параметрами $i и $n; вызвать шаблон с параметрами {$i = 0, $N = 5}; пусть сам шаблон вызывается рекурсивно с параметрами {$i + 1, $N} до $i > $N.

Пример:

<xsl:template match="/">
<output>
    <!-- stuff before -->
    <xsl:call-template name="block-generator">
        <xsl:with-param name="N" select="5"/>
    </xsl:call-template>
    <!-- stuff after -->
</output>
</xsl:template>

<xsl:template name="block-generator">
    <xsl:param name="N"/>
    <xsl:param name="i" select="0"/>
    <xsl:if test="$N >= $i">
        <!-- generate a block -->
        <fo:block>
            <xsl:value-of select="$i"/>
        </fo:block>     
        <!-- recursive call -->
        <xsl:call-template name="block-generator">
            <xsl:with-param name="N" select="$N"/>
            <xsl:with-param name="i" select="$i + 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>
Другие вопросы по тегам