user1746050
user1746050

Reputation: 2575

edit value of existing variable xpath

I have a variable which I set the value. Thereafter, in a loop, I would like to edit this variable and use it in the next iteration. Is there any way I can edit the value in the variable? Thanks!

<xsl:variable name="numberVariable" select="5">
----loop----
$numberVariable = $numberVariable+2
----End loop----

Upvotes: 1

Views: 1082

Answers (2)

Flynn1179
Flynn1179

Reputation: 12075

Look into the xsl:number element- you can do something like this:

<xsl:variable name="numberVariable">
  <xsl:number/>
</xsl:variable>
<xsl:value-of select="$numberVariable * 2"/>

It's difficult to know exactly what you're doing from your question, but hopefully this can point you in the right direction.

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243459

in a loop, I would like to edit this variable and use it in the next iteration. Is there any way I can edit the value in the variable?

The answer is negative:

XSLT is a functional language and this, among other things means that a variable's value, once defined, is immutable.

One can achieve the same effect in a more safe way, by calling another callable unit of the language (template or function) and passing the wanted new value as an argument.

I. Here is a simple example. The following transformation calculates the factorial of the integer, obtained from the string value of the document (top) element of the source XML document:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="/*/text()" name="factorial">
    <xsl:param name="pN" select="."/>
    <xsl:param name="pAccum" select="1"/>

    <xsl:value-of select="substring($pAccum, 1 div not($pN > 1))"/>
    <xsl:apply-templates select="self::node()[$pN > 1]">
      <xsl:with-param name="pN" select="$pN -1"/>
      <xsl:with-param name="pAccum" select="$pAccum * $pN"/>
    </xsl:apply-templates>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following source XML document:

<t>9</t>

the wanted, correct result is produced:

362880

In the code above, we see how the value of the parameter $pN is "decreased" from call to call, until it reaches 1, and the value of the parameter $pAccum is multiplied on each call by the value of the parameter $pN.

Do note, however, that we do not modify any parameter at all -- on each call a new instance of the parameter(s) is created, having the same name(s), but living only in the inner scope (call).


II. Often we can avoid the need for recursion: The following XSLT 1.0 transformation calculates and outputs the cubes of the numbers from 1 to 20:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:variable name="vDoc" select="document('')"/>

  <xsl:template match="/*" name="cubes">
    <xsl:param name="pN" select="20"/>

    <xsl:for-each select=
    "($vDoc//node() | $vDoc//node()/@* | $vDoc//namespace::*)
                                             [not(position() > $pN)]">
      <xsl:variable name="vM" select="position()"/>

      <xsl:value-of select="$vM*$vM*$vM"/>
      <xsl:text>&#xA;</xsl:text>                                            
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on any source XML document (not used), the wanted, correct result is produced:

1
8
27
64
125
216
343
512
729
1000
1331
1728
2197
2744
3375
4096
4913
5832
6859
8000

Do note how the standard XPath function position() is called to produce the "loop counter" :). This is the well known method of Piez.

Starting from XSLT versions 2.0 and above, no such tricks are necessary. One can simply write the following, using an XPath 2.0 range expression:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:variable name="vDoc" select="document('')"/>

  <xsl:template match="/*" name="cubes">
    <xsl:param name="pN" select="20"/>

    <xsl:for-each select="1 to $pN">
      <xsl:value-of select=". * . *."/>
      <xsl:text>&#xA;</xsl:text>                                            
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 2

Related Questions