其他人已经解释了变量是如何不可变的——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>
(注意:空白代码格式不会出现在您的结果中,因为只有空白的文本节点会自动从样式表中删除。所以不要觉得有必要将指令放在同一行。)
这种方法的一大优势(相对于 using 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>
and<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>
and <xsl:call-template/>
),而且还可以立即清楚当前节点是什么。您所要做的就是查看match
属性,并且您会立即知道您正在处理一个<section>
元素并且<section>
您正在计算元素。
有关模板规则(即<xsl:template>
具有match
属性的元素)如何工作的简要说明,请参阅“XSLT 工作原理”。