Mar*_*cel 27 xml xslt global-variables
我正在处理一个XML文件,我想要保留节点数,这样我就可以在编写新节点时将其用作ID.
目前我有一个名为'counter'的全局变量.我能够在模板中访问它,但我还没有找到在模板中操作它的方法.
这是我的XSLT文件的精简版本:
<xsl:variable name="counter" select="1" as="xs:integer"/>
<xsl:template match="/">
<xsl:for-each select="section">
<xsl:call-template name="section"></xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="section">
<!-- Increment 'counter' here -->
<span class="title" id="title-{$counter}"><xsl:value-of select="title"/></span>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
有什么建议怎么走?
Eva*_*enz 44
其他人已经解释了变量是如何不可变的 - 在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>
Run Code Online (Sandbox Code Playgroud)
(注意:空格代码格式化不会出现在结果中,因为只有空格的文本节点会自动从样式表中删除.所以不要觉得有必要将指令放在同一行上.)
这种方法的一大优势(与使用相反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>
Run Code Online (Sandbox Code Playgroud)
这是冗长的权衡(如果你仍然想要使用属性值模板花括号,需要一个额外的变量声明),但只是稍微如此,因为它也大大简化了你的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>
Run Code Online (Sandbox Code Playgroud)
这不仅是方法更简洁(<xsl:apply-templates/>取代了<xsl:for-each>和<xsl:call-template/>),但它也立即变得明确当前节点是什么.您所要做的就是查看match属性,然后您立即知道您正在处理一个<section>元素,并且<section>您正在计算元素.
有关模板规则(即<xsl:template>具有match属性的元素)如何工作的简洁说明,请参阅"XSLT如何工作".
XSLT变量无法更改.您将从模板到模板传递值.
如果您使用的是XSLT 2.0,则可以使用参数并使用隧道将变量传播到正确的模板.
您的模板将如下所示:
<xsl:template match="a">
<xsl:param name="count" select="0">
<xsl:apply-templates>
<xsl:with-param select="$count+1"/>
</xsl:apply-templates>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
如果要创建id,还要查看使用generate-id().
XSLT中的变量是不可变的,因此您必须考虑到这个问题.您可以position()直接使用:
<xsl:template match="/">
<xsl:for-each select="section">
<xsl:call-template name="section"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="section">
<span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
或者以更加面向模板的方式:
<xsl:template match="/">
<xsl:apply-templates select="section"/>
</xsl:template>
<xsl:template match="section">
<span class="title" id="title-{position()}"><xsl:value-of select="title"/></span>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)