在XSLT中,如何从不同的范围增加全局变量?

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如何工作".


BeW*_*ned 9

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().


jel*_*irt 6

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)