Другие уже объясняли, как переменные являются неизменяемыми - что в XSLT нет операторов присваивания (как в случае с чисто функциональными языками программирования в целом).
У меня есть альтернатива решениям, которые были предложены до сих пор. Это позволяет избежать передачи параметров (что многословно и безобразно в XSLT - даже я это признаю).
В XPath вы можете просто посчитать количество <section>
элементов, предшествующих текущему:
<xsl:template name="section">
<span class="title" id="title-{1 + count(preceding-sibling::section)}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
(Примечание: форматирование кода пробела не будет отображаться в вашем результате, так как текстовые узлы, содержащие только пробелы, автоматически удаляются из таблицы стилей. Не стесняйтесь помещать инструкции в одну строку.)
Одно большое преимущество этого подхода (в отличие от использования position()
) заключается в том, что он зависит только от текущего узла, а не от текущего списка узлов. Если вы каким-то образом изменили свою обработку (например, <xsl:for-each>
обработал не только разделы, но и какой-либо другой элемент), то значение position()
больше не обязательно будет соответствовать позиции элементов <section>
в вашем документе. С другой стороны, если вы используете count()
, как указано выше, то оно всегда будет соответствовать положению каждого элемента <section>
. Такой подход уменьшает связь с другими частями вашего кода, что, как правило, очень хорошая вещь.
Альтернативой count () было бы использование инструкции <xsl:number>
. Поведение по умолчанию нумерует все элементы с одинаковыми именами на одном уровне, что, как вам и нужно
<xsl:template name="section">
<xsl:variable name="count">
<xsl:number/>
</xsl:variable>
<span class="title" id="title-{$count}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
Это компромисс в многословии (требуется дополнительное объявление переменной, если вы все еще хотите использовать фигурные скобки шаблона значения атрибута), но лишь незначительно, поскольку это также значительно упрощает ваше выражение XPath.
Еще больше возможностей для улучшения. Хотя мы удалили зависимость от текущего списка узлов, мы все еще зависимы от текущего узла. Это само по себе неплохо, но не сразу видно из шаблона, каким является текущий узел. Все, что мы знаем, это то, что шаблон называется "section
"; Чтобы точно знать, что именно обрабатывается, мы должны посмотреть в другом месте нашего кода. Но даже это не должно быть так.
Если вы когда-нибудь почувствовали необходимость использовать <xsl:for-each>
и <xsl:call-template>
вместе (как в вашем примере), отступите назад и выясните, как использовать <xsl:apply-templates>
вместо этого.
<xsl:template match="/doc">
<xsl:apply-templates select="section"/>
</xsl:template>
<xsl:template match="section">
<xsl:variable name="count">
<xsl:number/>
</xsl:variable>
<span class="title" id="title-{$count}">
<xsl:value-of select="title"/>
</span>
</xsl:template>
Этот подход не только менее многословен (<xsl:apply-templates/>
заменяет <xsl:for-each>
и <xsl:call-template/>
), но также сразу становится ясно, что является текущим узлом. Все, что вам нужно сделать, это взглянуть на атрибут match
, и вы сразу узнаете, что обрабатываете элемент <section>
, а элементы <section>
- это то, что вы считаете.
Краткое объяснение того, как работают правила шаблонов (т. Е. <xsl:template>
элементы с атрибутом match
), см. «Как работает XSLT» .