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

时间:2009-05-07 06:15:51

标签: 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>

有任何建议如何从这里开始?

8 个答案:

答案 0 :(得分: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>

(注意:空格代码格式化不会出现在结果中,因为只有空格的文本节点会自动从样式表中删除。所以不要觉得强迫将指令放在同一行上。)

这种方法的一大优势(与使用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元素)如何工作的简明说明,请参阅"How XSLT Works"

答案 1 :(得分:8)

无法更改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>

如果要创建ID,请查看使用generate-id()。

答案 2 :(得分: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>

或者采用更加模板化的方式:

<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>

答案 3 :(得分:2)

变量是本地范围的,只能在xslt中读取。

答案 4 :(得分:2)

根据您的XSLT处理器,您可以将脚本功能引入XLST。例如,Microsoft XML库支持包含javascript。有关示例,请参阅http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspx。如果您计划在公共客户端浏览器上部署/执行XSLT,这种策略显然不会起作用;它必须由特定的XSLT处理器完成。

答案 5 :(得分:1)

您可以使用position()函数执行您想要的操作。它看起来像这样。

<xsl:template match="/">
  <xsl:for-each select="section">
    <xsl:call-template name="section">
      <xsl:with-param name="counter" select="{position()}"/>
    </xsl:call-template>
  </xsl:for-each>
</xsl:template>

<xsl:template name="section">
  <xsl:param name="counter"/>
  <span class="title" id="title-{$counter}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

答案 6 :(得分:0)

我自己没试过,但你可以尝试将参数传递给模板。 在你的第一个模板中,你可以在for-each语句中将参数设置为count()(或者current()可能?),然后将该值传递给“section”模板。

passing parameters to templates

上有更多内容

答案 7 :(得分:0)

使用<xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> $ RowNum 作为递增值。

例如:<xsl:template name="ME-homeTiles" match="Row[@Style='ME-homeTiles']" mode="itemstyle"> <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" /> ...<a href="{$SafeLinkUrl}" class="tile{$RowNum}"><img ....></a>

这将为值tile1,tile2,tile3等链接创建类...

相关问题