[ACCEPTED]-In XSLT how do I increment a global variable from a different scope?-global-variables

Accepted answer
Score: 44

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

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

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

<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 48 formatting won't appear in your result, as 47 whitespace-only text nodes get stripped 46 from the stylesheet automatically. So don't 45 feel compelled to put instructions on the 44 same line.)

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

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

<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 27 verbosity (requiring an additional variable 26 declaration if you still want to use the 25 attribute value template curly braces), but 24 only slightly so, as it also drastically 23 simplifies your XPath expression.

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

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

<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 9 approach less verbose (<xsl:apply-templates/> replaces both <xsl:for-each> and 8 <xsl:call-template/>), but it also becomes immediately clear 7 what the current node is. All you have to 6 do is look at the match attribute, and you instantly 5 know that you're processing a <section> element and 4 that <section> elements are what you're counting.

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

Score: 9

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

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

Your 3 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 2 look at using generate-id() if you want 1 to create ids.

Score: 6

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

<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 1 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>
Score: 2

variables are locally scoped and read only 1 in xslt.

Score: 2

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

Score: 1

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

<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>
Score: 0

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

Here's 1 more on passing parameters to templates

Score: 0

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

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>

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

More Related questions