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() < 7]">
<fo:block>
<xsl:value-of select="position() - 1"/>
</fo:block>
</xsl:for-each>
или если вы не обязательно знаете, что во входном документе будет хотя бы 6 узлов, вы можете использовать document('')
обрабатывать саму таблицу стилей как другой входной документ.
<xsl:for-each select="document('')/descendant::node()[position() < 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>