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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/833118/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 12:29:02  来源:igfitidea点击:

In XSLT how do I increment a global variable from a different scope?

xmlxsltglobal-variables

提问by Marcel

I am processing an XML file where I want to keep count of the number of nodes, so that I can use it as an ID as I write new nodes.

我正在处理一个 XML 文件,我想在其中记录节点数,以便在编写新节点时可以将其用作 ID。

At the moment I have a global variable called 'counter'. I am able to access it within a template, but I haven't found a way of manipulating it within a template.

目前我有一个名为“counter”的全局变量。我可以在模板中访问它,但我还没有找到在模板中操作它的方法。

Here is a condensed version of my XSLT file:

这是我的 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>

Any suggestions how to go from here?

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

回答by Evan Lenz

Others have already explained how variables are immutable--that there are no assignment statements in XSLT (as with purely functional programming languages in general).

其他人已经解释了变量如何是不可变的——XSLT 中没有赋值语句(就像一般的纯函数式编程语言一样)。

I have an alternative to the solutions that have been proposed so far. It avoids parameter passing (which is verbose and ugly in XSLT--even I'll admit that).

对于迄今为止提出的解决方案,我有一个替代方案。它避免了参数传递(这在 XSLT 中既冗长又丑陋——即使我承认这一点)。

In XPath, you can simply count the number of <section>elements that precede the current one:

在 XPath 中,您可以简单地计算<section>当前元素之前的元素数:

<xsl:template name="section">
  <span class="title" id="title-{1 + count(preceding-sibling::section)}">
    <xsl:value-of select="title"/>
  </span>
</xsl:template>

(Note: the whitespace code formatting won't appear in your result, as whitespace-only text nodes get stripped from the stylesheet automatically. So don't feel compelled to put instructions on the same line.)

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

One big advantage of this approach (as opposed to using position()) is that it's only dependent on the current node, not on the current node list. If you changed your processing somehow (e.g., so <xsl:for-each>processed not only sections but some other element too), then the value of position()would no longer necessarily correspond to the position of <section>elements in your document. On the other hand, if you use count()as above, then it will always correspond to the position of each <section>element. This approach reduces coupling with other parts of your code, which is generally a very good thing.

这种方法(相对于使用position())的一大优点是它只依赖于当前节点,而不依赖于当前节点列表。如果您以某种方式更改了处理方式(例如,<xsl:for-each>不仅处理了部分,还处理了其他一些元素),那么 的值position()将不再与<section>文档中元素的位置相对应。另一方面,如果你count()像上面那样使用,那么它总是对应每个<section>元素的位置。这种方法减少了与代码其他部分的耦合,这通常是一件非常好的事情。

An alternative to count() would be to use the <xsl:number>instruction. It's default behavior will number all like-named elements at the same level, which happens to be what you want:

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>

It's a trade-off in verbosity (requiring an additional variable declaration if you still want to use the attribute value template curly braces), but only slightly so, as it also drastically simplifies your XPath expression.

这是冗长的权衡(如果您仍然想使用属性值模板花括号,则需要额外的变量声明),但只是稍微如此,因为它还大大简化了您的 XPath 表达式。

There's yet more room for improvement. While we've removed dependency on the current node list, we still are dependent on the current node. That, in and of itself, is not a bad thing, but it's not immediately clear from looking at the template what the current node is. All we know is that the template is named "section"; to know for sure what's being processed, we have to look elsewhere in our code. But even that doesn't have to be the case.

还有更多的改进空间。虽然我们已经移除了对当前节点列表的依赖,但我们仍然依赖于当前节点。这本身并不是一件坏事,但通过查看模板并不能立即清楚当前节点是什么。我们只知道模板名为“ section”;要确定正在处理什么,我们必须查看代码中的其他地方。但即使如此也不必如此。

If you ever feel led to use <xsl:for-each>and <xsl:call-template>together (as in your example), step back and figure out how to use <xsl:apply-templates>instead.

如果您曾经觉得使用<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>

Not only is this approach less verbose (<xsl:apply-templates/>replaces both <xsl:for-each>and <xsl:call-template/>), but it also becomes immediately clear what the current node is. All you have to do is look at the matchattribute, and you instantly know that you're processing a <section>element and that <section>elements are what you're counting.

这种方法不仅不那么冗长(<xsl:apply-templates/>替换了<xsl:for-each><xsl:call-template/>),而且还可以立即清楚当前节点是什么。您所要做的就是查看match属性,您会立即知道您正在处理一个<section>元素,而这些<section>元素就是您正在计算的内容。

For a succinct explanation of how template rules (i.e. <xsl:template>elements that have a matchattribute) work, see "How XSLT Works".

有关模板规则(即<xsl:template>具有match属性的元素)如何工作的简要说明,请参阅“XSLT 的工作原理

回答by BeWarned

XSLT variables cannot be changed. You'll have pass the value along from template to template.

XSLT 变量不能更改。您必须将值从模板传递到模板。

If you are using XSLT 2.0, you can have parameters and use tunneling to propagate the variable to the right templates.

如果您使用 XSLT 2.0,您可以使用参数并使用隧道将变量传播到正确的模板。

Your template will look something like this:

您的模板将如下所示:

<xsl:template match="a">
<xsl:param name="count" select="0">
  <xsl:apply-templates>
     <xsl:with-param select="$count+1"/>
  </xsl:apply-templates>
</xsl:template>

Also look at using generate-id() if you want to create ids.

如果您想创建 id,还可以使用 generate-id()。

回答by jelovirt

Variables in XSLT are immutable so you have to approact the problem with that in mind. You could either use position()directly:

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>

Or in a more template orientated way:

或者以更面向模板的方式:

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

回答by Luixv

variables are locally scoped and read only in xslt.

变量是局部范围的,并且只能在 xslt 中读取。

回答by Alan McBee - MSFT

Depending on your XSLT processor, you may be able to introduce scripted functions into your XLST. For example, the Microsoft XML library supports the inclusion of javascript. See http://msdn.microsoft.com/en-us/library/aa970889(VS.85).aspxfor an example. This tactic obviously won't work if you're planning to deploy/execute XSLT on public client browsers; it has to be done by a specific XSLT processor.

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

回答by Bhushan Bhangale

You can use the position() function to do what you want. It would look something like this.

您可以使用 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>

回答by marika.daboja

Use <xsl:variable name="RowNum" select="count(./preceding-sibling::*)" />and $RowNumas an incrementing value.

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

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

例如: <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>

This will create classes for link with values tile1, tile2, tile3 etc...

这将为具有值 tile1、tile2、tile3 等的链接创建类...

回答by autonomatt

Haven't tried this myself, but you could try and pass a parameter to the template. In your first template you set the parameter to count() (or current() maybe?) within the for-each statement and then pass that value to your "section" template.

自己还没有尝试过,但您可以尝试将参数传递给模板。在您的第一个模板中,您在 for-each 语句中将参数设置为 count() (或 current() 也许?),然后将该值传递给您的“部分”模板。

Here's more on passing parameters to templates

这里有更多关于将参数传递给模板的内容