Variable scope in XSLT

14,227

Solution 1

Desired Output: Budapest london Rome

What you are after is grouping output by city name. There are two common ways to do this in XSLT.

One of them is this:

<xsl:template match="/allTrips">
  <xsl:apply-templates select="trip" />
</xsl:template>

<xsl:template match="trip">
  <!-- test if there is any preceding <trip> with the same <result> -->
  <xsl:if test="not(preceding-sibling::trip[result = current()/result])">
    <!-- if there is not, output the current <result> -->
    <xsl:copy-of select="result" />
  </xsl:if>
</xsl:template>

And the other one is called Muenchian grouping and @Rubens Farias just posted an answer that shows how to do it.

Solution 2

You can't change variables in XSLT.

You need to think about it more as functional programming instead of procedural, because XSLT is a functional language. Think about the variable scoping in something like this pseudocode:

variable temp = 5
call function other()
print temp

define function other()
  variable temp = 10
  print temp

What do you expect the output to be? It should be 10 5, not 10 10, because the temp inside the function other isn't the same variable as the temp outside that function.

It's the same in XSLT. Variables, once created, cannot be redefined because they are write-once, read-many variables by design.

If you want to make a variable's value defined conditionally, you'll need to define the variable conditionally, like this:

<xsl:variable name="temp">
  <xsl:choose>
    <xsl:when test="not(tourcode = 'a')">
      <xsl:text>b</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>a</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>
<xsl:if test="$temp = 'b'">
  <!-- Do something -->
</xsl:if>

The variable is only defined in one place, but its value is conditional. Now that temp's value is set, it cannot be redefined later. In functional programming, variables are more like read-only parameters in that they can be set but can't be changed later. You must understand this properly in order to use variables in any functional programming language.

Solution 3

Try this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="trip" match="trip" use="result" />

  <xsl:template match="/trips">

    <xsl:for-each select="trip[count(. | key('trip', result)[1]) = 1]">
      <xsl:if test="position() != 1">, </xsl:if>
      <xsl:value-of select="result"/>
    </xsl:for-each>

  </xsl:template>
</xsl:stylesheet>
Share:
14,227
Mazzi
Author by

Mazzi

SPARTAAAAAAAA!!!

Updated on June 13, 2022

Comments

  • Mazzi
    Mazzi almost 2 years

    I am having an issue trying to figure out var scoping on xslt. What I actually want to do it to ignore 'trip' tags that have a repeated 'tourcode'.

    Sample XML:

    <trip>
     <tourcode>X1</tourcode>
     <result>Budapest</result>
    </trip>
    <trip>
     <tourcode>X1</tourcode>
     <result>Budapest</result>
    </trip>
    <trip>
     <tourcode>X1</tourcode>
     <result>Budapest</result>
    </trip>
    <trip>
     <tourcode>Y1</tourcode>
     <result>london</result>
    </trip>
    <trip>
     <tourcode>Y1</tourcode>
     <result>london</result>
    </trip>
    <trip>
     <tourcode>Z1</tourcode>
     <result>Rome</result>
    </trip>
    

    XSLT Processor:

    <xsl:for-each select="trip">    
        <xsl:if test="not(tourcode = $temp)">
          <xsl:variable name="temp" select="tour"/>
          // Do Something (Print result!)
        </xsl:if>
    </xsl:for-each>
    

    Desired Output: Budapest london Rome