XSLT: can I declare a variable globally and later assign a value to it
Asked Answered
R

6

24

What can I do to make this code work?

<xsl:choose>
  <xsl:when test='type = 6'>
    <xsl:variable name='title' select='root/info/title' />
  </xsl:when>
  <xsl:when test='type = 7'>
    <xsl:variable name='title' select='root/name' />
  </xsl:when>
  <xsl:otherwise>
    <xsl:variable name='title'>unknown</xsl:variable>
  </xsl:otherwise>
</xsl:choose>

<div class='title'>
  <xsl:value-of select='$title'/>
</div>

This doesn't work because when i do <xsl:value-of select='$title'/>, $title is out of scope. I tried to add the line <xsl:variable name='title'/> outside of the scope, but that won't work either, because then when i call <xsl:variable name='title' select='root/info/title' /> for example, i already have set this variable before. How should I solve this?

Robinett answered 17/11, 2010 at 13:49 Comment(1)
Good question, +1. See my answer for three different solutions. :)Assemblyman
P
36

You can move the choose inside the setting of the variable, like this:

<xsl:variable name="title">
  <xsl:choose>
    <xsl:when test='type=6'>
      <xsl:value-of select="root/info/title" />
    </xsl:when>
    ...
  </xsl:choose>
</xsl:variable>

<div class='title'>
  <xsl:value-of select="$title" />
</div>
Packet answered 17/11, 2010 at 13:52 Comment(3)
yeah, that's what i was thinking too. I don't like this solution very much because i have over 20 types, that each need 5 variables. If i could list those 5 variables for every type the one after the other, that would make my code much more clear, and it would be easyer to add another type. With the solution you suggest, if have to add a line for the new type in each of the five variables. But I think there will be no other way right. I'll get over it, thanks for the answer :)Robinett
You could do a call-template inside each variable instead of a choose, and then have the choose in one template.Packet
Oh, but wait--then you'd have to check for which variable you're setting inside the template ;)Packet
A
15
<xsl:choose> 
  <xsl:when test='type = 6'> 
    <xsl:variable name='title' select='root/info/title' /> 
  </xsl:when> 
  <xsl:when test='type = 7'> 
    <xsl:variable name='title' select='root/name' /> 
  </xsl:when> 
  <xsl:otherwise> 
    <xsl:variable name='title'>unknown</xsl:variable> 
  </xsl:otherwise> 
</xsl:choose> 

<div class='title'> 
  <xsl:value-of select='$title'/> 
</div> 

This doesn't work

This is a FAQ:

You are defining several variables, each named $title and each useless, because it goes out of scope immediately.

The proper way in XSLT 1.0 to define a variable based on conditions is:

<xsl:variable name="vTitle">
    <xsl:choose>
      <xsl:when test='type = 6'>
        <xsl:value-of select='root/info/title' />
      </xsl:when>
      <xsl:when test='type = 7'>
        <xsl:value-of  select='root/name' />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="'unknown'"/>
      </xsl:otherwise>
    </xsl:choose>
</xsl:variable>

Another way of defining the same variable: In this particular case you want the variable to have a string value. This can be expressed in a more compact form:

<xsl:variable name="vTitle2" select=
"concat(root/info/title[current()/type=6],
        root/name[current()/type=7],
        substring('unknown', 1 div (type > 7 or not(type > 5)))
       )
"/>

Finally, in XSLT 2.0 one can define such a variable even more conveniently:

<xsl:variable name="vTitle3" as="xs:string" select=
 "if(type eq 6)
    then root/info/title
    else if(type eq 7)
            then root/name
            else 'unknown'
 "/>
Assemblyman answered 17/11, 2010 at 14:13 Comment(1)
because it goes out of scope immediately. I suspected this to be the case. What tag defines the scope, xsl:choose? My problem is that I try to set around 50 variables depending on a single test, and I don't want each variable to repeat the test. I tried to do it logically similar to (choose,(when(a),variable*),(when(b), variable*) ) and so I did go out of scope... is there a way to use a single test for multiple variables but still keep them in scope?Mellen
H
4

You can't re-assign variables in XSLT (1.0). The name is probably not luckily chosen; an xsl:variable is more a symbol than a variable.

In your sample you can use the following:

<xsl:variable name='title'>
  <xsl:choose>
    <xsl:when test='type = 6'>
      <xsl:value-of select='root/info/title' />
    </xsl:when>
    <xsl:when test='type = 7'>
      <xsl:value-of select='root/name' />
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>unknown</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>
Hoicks answered 17/11, 2010 at 13:52 Comment(2)
It's a variable, not a constant literal. But it's a variable in declarative paradigm: more like a variable in an equation.Remington
@Alejandro: Yes, I was referring to symbol/variable as in the mathematical sense as described by Michael Kay: "Some people have asked, why call it a variable if you can't vary it? The answer lies in the traditional mathematical use of the word variable: a variable is a symbol that can be used to denote different values on different occasions." (gandhimukul.tripod.com/xslt/facts.html)Hoicks
R
2

Besides @carolclarinet's standar answer and more compact @Dimitre's answer, when the result depend on some node and you are worry about reuse and extensibility, you can apply templates and use pattern matching, i.e:

<xsl:variable name="title"> 
    <xsl:apply-templates select="type" mode="title"/> 
</xsl:variable>

<xsl:template match="type[.=6]" mode="title"> 
    <xsl:value-of select='../root/info/title"/> 
</xsl:template> 
<xsl:template match="type[.=7]" mode="title"> 
    <xsl:value-of select='../root/name"/> 
</xsl:template> 
<xsl:template match="type" mode="title"> 
    <xsl:text>unknown</xsl:text> 
</xsl:template>
Remington answered 17/11, 2010 at 14:45 Comment(0)
C
-1

I'm doing it to save local value to xml xsl:desult_document to folder /temp_variables_loop/loop-data-id_(position()).xml.

And in next loop I read files from /temp_variables_loop.alias/loop-data-id_(position()-1).xml.

You need to cheat xsl engine by make alias of /temp_variables_loop to temp_variables_loop.alias because you cannot read and write to the same file in one xsl.

Calaboose answered 10/2, 2017 at 18:36 Comment(0)
A
-1

Yes, you can do that in xlst 2.0, but it's forbidden in xlst 1.0 version.

the code following can run well in my pc with xslt processor saxson-he 9.8.0.12 .

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    exclude-result-prefixes="xs map"
    version="2.0">

    <xsl:template match="/">
        <xsl:variable name="i1" select="123" as="xs:integer"/>
        <xsl:variable name="s1" select="'abcd'" as="xs:string"/>
        <xsl:variable name="d1" select="234.5" as="xs:float"/>

        <!-- we test that variable v1 can be assignment multi times and it is ok. -->
        <xsl:variable name="v1" select="$i1"/>
        v1 is: <xsl:value-of select="$v1"/>
        <xsl:variable name="v1" select="$s1"/>
        v1 is: <xsl:value-of select="$v1"/>
        <xsl:variable name="v1" select="$d1"/>
        v1 is: <xsl:value-of select="$v1"/>

        <xsl:variable name="map1" select="map{'haha':119, 'good':110}"/>
        <xsl:variable name="map2" select="map:put($map1, 'go', 122)"/>
        <xsl:variable name="map1" select="map:put($map2, 'hello', 999)"/>
        map1(haha) is <xsl:sequence select="$map1?haha"/>
        map1(go) is <xsl:sequence select="$map1?go"/>
        map1(hello) is <xsl:sequence select="$map1?hello"/>
    </xsl:template>

</xsl:stylesheet>

reslut of output is that:

v1 is: 123 v1 is: abcd v1 is: 234.5 map1(haha) is 119 map1(go) is 122 map1(hello) is 999

In xslt 1.0, it need recursion function, then pass the value as param.

Ahumada answered 2/12, 2019 at 3:39 Comment(1)
No, you can't - see my comment to your other post with the same claim: #18637719Buttress

© 2022 - 2024 — McMap. All rights reserved.