Accessing a variable outside the for loop in xsl - xslt

I am setting a variable as shown below..
<xsl:variable name="FoundFloating"> <xsl:value-of select="'no'" />
</xsl:variable>
Now I am doing some processing as shown below ..
<xsl:if test="$abcid=$def_id">
<xsl:for-each "$abcd">
<xsl:variable name="abcRate"> <xsl:value-of select="./def_Period/"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$abcdf !=$abcRate">
<xsl:variable name="$FoundFloating"> <xsl:value-of select="yes" />
</xsl:variable>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Now after this xsl for I am evaluating as shown below.. But my query is that whether foundfloating variable is accessible as the for loop is already ended..
<xsl:choose>
<xsl:when test="$FoundFloating='yes'"> <xsl:value-of select="'AAA'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'BBBA'" />
</xsl:otherwise>
</xsl:choose>
Now after this xsl for I am evaluating as shown below.. But my query is that whether foundfloating variable is accessible as the for loop is already ended..
Please advise on this as I have updated the post

The first thing to note is that variables are immutable in XSLT. This means once declared, they cannot be changed. What is actually happening in your XSLT sample is you are declaring a whole new variable, with the same name.
The FoundFloating variable you are declaring within the for loop will not be accessible outside the for loop, as it will only be local in scope. In fact, it will only be accessible inside the xsl:when statement it is defined in. It is a different variable to the global one you defined, and only exists in the loop.
You don't really need the loop here. You can combine the condition in the xsl:for-each and the condition in the xsl:when into a single variable declaration.
<xsl:if test="$abcid=$def_id">
<xsl:variable name="FoundFloating">
<xsl:if test="$abcd[def_Period != $abcdf]">yes</xsl:if>
</xsl:variable>
<xsl:choose>
<xsl:when test="$FoundFloating='yes'">
(This replaces the xsl:for-each statement entirely)
In fact, this can be simplified even more, by simply setting FoundFloating to the node itself (if there is one), rather than "yes"
<xsl:if test="$abcid=$def_id">
<xsl:variable name="FoundFloating" select="$abcd[def_Period != $abcdf]" />
<xsl:choose>
<xsl:when test="$FoundFloating">
This works because the essence of the test is whether a certain node exists matching the condition. Rather than setting a variable to "Yes" or "No" if it exists or not, the variable is set to the node itself. Then, if it does the exist the statement <xsl:when test="$FoundFloating"> returns true, but false if it doesn't.
So, you don't need the xsl:for-each loop, and you only need to declare the FoundFloating variable once.

Related

xsl:param used without being assigned a value

I am looking at this xslt template:
<xsl:template match="row">
<xsl:param name="spans"/>
<xsl:param name="browserows"/>
<xsl:choose>
<xsl:when test="contains($spans, '0')">
<xsl:call-template name="normal-row">
<xsl:with-param name="spans" select="$spans"/>
<xsl:with-param name="browserows" select="$browserows"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
I am trying to understand where the value of $spans is coming from at the test statement on line 6, but it looks like the value was never assigned.
I cannot find spans as a global param anywhere.
Am I missing something?
It would come from the calling code that executed the <xsl:apply-templates> that matched against that row - because the <xsl:template> declares that parameter in <xsl:param name="spans"/>. If you didn't specify a value via <xsl:with-param>, then it is presumably nil.

Choose with for-each inside?

I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
If xsl were a procedural language where variables could be reassigned, I'd use something like this:
<xsl:variable name="copyAttrib" select="true()">
<xsl:for-each select="tokenize($ignoreAttributes,',')">
<xsl:if test="compare(., name()) != 0">
<xsl:variable name="copyAttrib" select="false()"/>
</xsl:if>
</xsl:for-each>
Unfortunately, I can't do that, because xsl is functional (so says this other answer). So variables can only be assigned once.
I think the solution would look something like:
<vsl:variable name="copyAttrib">
<xsl:choose>
<xsl:when>
<xsl:for-each select="tokenize($ignoreAttributes, ',')">
<xsl:if test="compare(., name()) != 0"/>
</xsl:for-each>
<xsl:otherwise>
<xsl:value-of select="false()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Obviously not exactly that (otherwise I wouldn't be asking.)
I know that I could bypass the tokenize and for-each loop by just using replaces on ignoreAttributes and changing all the , to | and then using matches, but I'd like to avoid that if possible because then I need to deal with the possibility that ignoreAttributes (which the user provides) might contain some special characters that will change the regex pattern and escape them all.
I have a parameterignoreAttributes which is a comma separated list of things to look for. I want to set a variable copyAttrib to be equal to whether any of them are exactly matched by name().
That sounds to me like
<xsl:variable name="copyAttrib" as="xs:boolean"
select="tokenize($parameterignoreAttributes, ',') = name()"/>
You say:
Unfortunately, I can't do that, because xsl is functional
when what you mean is: "Fortunately, I don't need to do that, because XSLT is functional".
An XSLT-1.0 way of doing this is by using a recursive, named template:
<xsl:template name="copyAttrib">
<xsl:param name="attribs" />
<xsl:choose>
<xsl:when test="normalize-space(substring-before($attribs,',')) = normalize-space(name(.))">
<xsl:value-of select="'true'" />
</xsl:when>
<xsl:when test="normalize-space($attribs) = ''">
<xsl:value-of select="'false'" />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="substring-after($attribs,',')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Apply this template onto the current, the selected, node and wrap it in a <xsl:variable>:
<xsl:variable name="copyAttribResult">
<xsl:call-template name="copyAttrib">
<xsl:with-param name="attribs" select="'a,b,c,...commaSeparatedValues...'" />
</xsl:call-template>
</xsl:variable>
to get either true or false as a result.

Setting a value in a variable on if conditions using XSL

I have a basic condition that checks if a variable is empty and if it is set the variable to a specific value like so.
<xsl:variable name="PIC" select="avatar"/>
<xsl:choose>
<xsl:when test="avatar !=''">
<xsl:variable name="PIC" select="avatar"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="PIC" select="'placeholder.jpg'"/>
</xsl:otherwise>
</xsl:choose>
Basically var PIC is set to whatever avatar returns. Then a test is carried to check if if it's not empty and assigned to var PIC and if it is empty a value placeholder.jpg is added to var PIC instead.
Now for some reason I keep getting the following warning
A variable with no following sibling instructions has no effect
Any ideas on what I am doing wrong here?
Variables are immutable in XSLT, and cannot be changed once set. The variable declarations in the xsl:choose are simply new declarations that at local in scope to the current block. (They are said to "shadow" the initial variable).
What you need to do is this...
<xsl:variable name="PIC">
<xsl:choose>
<xsl:when test="avatar !=''">
<xsl:value-of select="avatar"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'placeholder.jpg'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Use a single xsl:variable as <xsl:variable name="PIC" select="if (avatar != '') then avatar else 'placeholder.jpg'"/>.

Understaning recursion in xslt

I am trying to understand recorsion in xslt. Can anybody explain what's happening in this code.
<xsl:template name="factorial">
<xsl:param name="number" select="1"/>
<xsl:choose>
<xsl:when test="$number <= 1">
<xsl:value-of select="1"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="recursive_result">
<xsl:call-template name="factorial">
<xsl:with-param name="number" select="$number - 1"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$number * $recursive_result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I can't understand why we wrap factorial template with <xsl:variable name="recursive_result">.
If there is more clear example is available, please guide me to that. I am lack of knowledge in recursion.
The call-template element is wrapped with the variable element in order to assign the result of calling it to the variable recursive_result.
This is done so that it can then be multiplied by number on the following line, to produce the final result.
You can't declare global variables in XSLT that are changeable from other parts of the script. If you need a result from a template call or a recursion is the only way to "print out" the generated result in a variable.
The "print out" is done with the <xsl:value-of ... statement.
In XSLT, we are using recursion instead of Looping. Recursion is nothing but a particular type of function that calls itself as many times when required to find the final solution. So,
input the number variable as '1'
The given value if it is less than 1 then it simply print the value of $number
otherwise, it is move to call-template as input for the variable number with help of with-param
here, it is calling the same templates again and pass value to same variable named as number
Then the result value will be assigned to the variable recursive_result
Hope would be understand.

XSl:Variable - Condition to check whether value exist

Using XSLT 1.0, how do I check whether the value in the variable exists or not?
I am assigning the value to the variable initially from my XML data and then need to check whether it exits or not:
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./RootTag/*[1])"/>
</xsl:variable>
<xsl:if test="string($DOC_TYPE) = ''">
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./*[1])"/>
</xsl:variable>
</xsl:if>
The above is not working as expected. What I need is if <RootTag> exists in my data then the variable should contain the child node below the <RootTag>. If <RootTag> does not exist then the DOC_TYPE should be the first Tag in my XML data.
Thanks for your response.
You can't re-assign variables in XSLT. Variables a immutable, you can't change their value. Ever.
This means you must decide within the variable declaration what value it is going to have:
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="RootTag">
<xsl:value-of select="name(RootTag/*[1])" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name(*[1])" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
A few other notes:
this: './RootTag' is redundant. Every XPath you don't start with a slash is relative by default, so saying 'RootTag' is enough
this: <xsl:value-of select="name(*[1])"/> already results in a string (names are strings by definition), so there is no need to do <xsl:if test="string($DOC_TYPE) = ''"> , a simple <xsl:if test="$DOC_TYPE = ''"> suffices
to check if a node exists simply select it via XPath in a test="..." expression - any non-empty node-set evaluates to true
XSLT has strict scoping rules. Variables are valid within their parent elements only. Your second variable (the one within the <xsl:if>) would go out of scope immediately(meaning right at the </xsl:if>).
Try this
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="/RootTag"><xsl:value-of select="name(/RootTag/*[1])"></xsl:value-of></xsl:when>
<xsl:otherwise><xsl:value-of select="name(/*[1])"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
It only exists if you have assigned it. There's no reason to test it for existence.
See also here