How to access global variable value in multiple template tags - xslt

I have created a global variable and its been used in two templates I am able to access i first template, not able to get the value in second template . Below are my workings
<xsl:variable name="currentValue"></xsl:variable> //global variable declaration
<xsl:template match="/">
<xsl:variable name="unique-accounts" select="/*/*/*/accountId/text()generate-id()=generate-id(key('account-by-id', .)[1])]"/>
<xsl:for-each select="$unique-accounts">
<xsl:variable name="currentValue" select="current()"/>
<xsl:value-of select="$currentValue"/> //here value is printing
<xsl:apply-templates select="//secondTemplate"/>
</xsl:for-each>
</xsl:template> //close od first template
<xsl:template match="secondTemplate">
<xsl:value-of select="$currentValue"/> //here value is not printing
</xsl:template>

If I follow the logic of your code correctly (which is not at all certain), you have declared a global variable as:
<xsl:variable name="currentValue"></xsl:variable>
i.e. as empty. You are then calling this global variable inside your second template:
<xsl:template match="secondTemplate">
<xsl:value-of select="$currentValue"/>
</xsl:template>
and getting an empty result - which is exactly what you should expect.
Within your first template, the declaration:
<xsl:variable name="currentValue" select="current()"/>
overrides the global variable declaration for the scope of the template (more precisely, for the following siblings of the declaration and their descendants - but since the declaration is the first thing you do in the template, it comes down to the same thing).
In more technical terms, the binding established within the template shadows the binding established by the top-level xsl:variable element:
http://www.w3.org/TR/xslt/#dt-shadows

Variables in XSLT are named values, they are not memory locations in which you can place different values at different times. That's a fundamental difference between declarative and procedural programming.
If you would like to explain the problem you are trying to solve (that is, the input and output of the transformation) then I'm sure we can explain how to write it in XSLT. Reverse-engineering the requirement from a completely wrong approach to the solution isn't possible.

Related

Declaring multiple XSLT variables of the same name in the same scope

I know that XSLT variables once initialized cannot change their respective values.
Saxon-PE 9.2.0.6 allows the following syntax:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:variable name="myVar" select="'asdex upgrade'" />
<xsl:value-of select="$myVar" /><xsl:text>
</xsl:text>
<xsl:variable name="myVar" select="'Wendelstein'" />
<xsl:value-of select="$myVar" /><xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
with this output:
asdex upgrade
Wendelstein
So what is happening here? Obviously this code is not in contradiction to the fact that variables are immutable in XSLT, or else the Saxon processor would not process it. Am I creating two variables with the same name here (with their scopes local to the given template)? If so, do I have means to access the first variable after the second one was created, or did it cease to exist, or is it overshadowed in the XSLT processor's symbol tables? Or what gives?
Thanks for any enlightenment!
See https://www.w3.org/TR/xslt20/#scope-of-variables on details, it explains
For any variable-binding element, there is a region (more
specifically, a set of element nodes) of the stylesheet within which
the binding is visible. The set of variable bindings in scope for an
XPath expression consists of those bindings that are visible at the
point in the stylesheet where the expression occurs.
and
A local variable binding element is visible for all following siblings
and their descendants, with two exceptions: it is not visible in any
region where it is shadowed by another variable binding, and it is not
visible within the subtree rooted at an xsl:fallback instruction that
is a sibling of the variable binding element. The binding is not
visible for the xsl:variable or xsl:param element itself.
[Definition: A binding shadows another binding if the binding occurs
at a point where the other binding is visible, and the bindings have
the same name. ] It is not an error if a binding established by a
local xsl:variable or xsl:param shadows a global binding. In this
case, the global binding will not be visible in the region of the
stylesheet where it is shadowed by the other binding.
So the second xsl:variable name="myVar" shadows the first one.

XSLT static key declaration using sequence constructor instead of #use?

XSLT2.0 seems to allow declaring key inline, inside the <key> element.
All the examples I have seen declare an intermediate XML fragment and match on that, using #use. I think that is wasteful.
Can you please provide an example of a XSLT 2.0 key declaration using sequence constructor inside the key element rather than #use?
Usually the value that you want to index is a very simple function of the objects being indexed, so the #use attribute works perfectly well. You can use a contained sequence constructor for more complex cases if you need to, but I've very rarely seen it needed. For example you might want to index sections by their section number like this:
<xsl:key name="k" match="section">
<xsl:number level="multi" count="section" format="1.1.1"/>
</xsl:key>
I don't know what makes you think that using the #use attribute is "wasteful".
I don't think I have used that feature so far and I can't think of a good sample for an obvious use case but let's assume foo elements have some value child elements and we want to sort the value elements and only key on the first or last few in sort order so we could use e.g.
<xsl:key name="by-first-three-values" match="foo">
<xsl:for-each select="value/xs:decimal(.)">
<xsl:sort select="."/>
<xsl:if test="position() lt 4">
<xsl:sequence select="."/>
</xsl:if>
</xsl:for-each>
</xsl:key>
Of course you could avoid that use by writing a function that sorts with perform-sort and then call that function in use="mf:sort(value)[position() lt 4]" but I guess there is at least the flexibility to do it inline of the xsl:key.
What I am after is even more simple, something similar to:
<xsl:key name="AcronymKey" match="a:acronymItem" use="a:acronym"/>
<xsl:template name="AcronymnStandsFor">
<xsl:param name="acronym"/>
<!-- change context to current document so the key will work -->
<xsl:for-each select="document('')">
<xsl:value-of select="key('AcronymKey',$acronym)/a:standsFor"/>
</xsl:for-each>
</xsl:template>
<a:acronymList>
<a:acronymItem>
<a:acronym>Ant</a:acronym>
<a:standsFor>Another Neat Tool</a:standsFor>
</a:acronymItem>
</a:acronymList>
But where the actual key is inside the key element. Is that possible, given the syntax?

Is there a better way than xsl:variable to refer to an attribute value inside an XPath expression?

I am using XSLT 2.0 to transform some XML. The source XML looks similar to this:
<AnimalTest>
<AnimalTypes>
<AnimalType name="cat"/>
<AnimalType name="dog"/>
</AnimalTypes>
<Animals>
<Animal name="Sylvester" typeName="cat"/>
<Animal name="Fido" typeName="dog"/>
<Animal name="Tom" typeName="cat"/>
</Animals>
</AnimalTest>
Inside the XSL template to handle AnimalType tags, I want to use the name attribute of the AnimalType inside an XPath expression. The only way I have been able to achieve this, is by introducing a variable that holds the attribute #name and is referred from inside the XPath expression, like this:
<xsl:template match="AnimalType">
<xsl:variable name="typename" select="#name"/>
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=$typename]"/>
</xsl:template>
This works, but I wonder whether I really have to use this temporary variable. Is there any better way to refer to that #name attribute? It looks like a detour to me.
If you really disliked using the variable, you could use the current() function to refer to the current context node (AnimalType in your case)
<xsl:apply-templates select="/AnimalTest/Animals/Animal[#typeName=current()/#name]"/>
If you had a more complex expression, using a variable can improve readability though, and you could potentially re-use in other places.
One thing to note is that his declaration
<xsl:variable name="typename" select="#name"/>
Is not quite the same as this declaration
<xsl:variable name="typename">
<xsl:value-of select="#name" />
</xsl:variable>
Although both variables will contain the same value. In the latter case (using xsl:value-of) you are creating a copy of the value of the name attribute. In the former case, you are referring to the attribute directly. Therefore using the latter format would be less efficient.
As a slight aside, you may consider using a key here to look up your Animal elements by their typeName
<xsl:key name="AnimalByType" match="Animal" use="#typeName" />
That way, your apply-templates expression can be simplified to just the following
<xsl:template match="AnimalType">
<xsl:apply-templates select="key('AnimalByType', #name)"/>
</xsl:template>

How to set XSLT variable to contain exact xml data

I wonder how to store xml data from one variable in another.
This works ($oldvariable contains xml data):
<xsl:variable name="newvariable" select="$oldvariable"/>
But this does not work (probably because of some obvious reason for an experienced XSLT-coder):
<xsl:variable name="newvariable">
<xsl:copy-of select="$oldvariable"/>
</xsl:variable>
How can I make the latter store the exact variable data?
I need that construct since I'm really using a :
<xsl:variable name="newvariable">
<xsl:choose>
<xsl:when test="some-test">
<xsl:copy-of select="$oldvariable"/>
...
Thanks alot!
This is FAQ: In XSLT 1.0, whenever you declare a variable/parameter with content template (without #select), the result type is Result Tree Fragment.
Then, you can't use RTF as left hand for / step operator.
So, how do you declare a variable to be one of two node-sets based on a condition?
<xsl:variable name="newvariable" select="$oldvariable[$condition]|
$othernodeset[not($condition)]"/>

Select template for execution using a condition including variable on apply-templates

I have a template that looks like below
<xsl:template match="more-info" mode="docuSection">
html
</xsl:template>
and which is applied with the call
<xsl:apply-templates select="." mode="docuSection"/>
so the template is applied when the current node has more-info element, is there a way to make this template get applied with the same call and with the condition which includes a global variable e.g. match="$mode='edit' or more-info"
Best Regards,
Keshav
is there a way to make this template
get applied with the same call and
with the condition which includes a
global variable e.g.
match="$mode='edit' or more-info"
In XSLT 2.0 this is perfectly legal:
<xsl:template match="more-info[$mode = ('edit', 'more-info')]"
mode="docuSection">
In XSLT 1.0 it is forbidden to use variable or key references within a match pattern.
However, one can use either of the following techniques:
I. Within the <xsl:apply-templates> instruction specify the exact node-list of nodes to be processed.
<xsl:apply-templates mode="docuSection"
select="self::*[$mode = 'edit' or $mode='more-info']" />
||. Make the match pattern more general, but do any processing within the template only if the desired condition is fulfilled:
<xsl:template match="more-info" mode="docuSection">
<xsl:if test="$mode = 'edit' or $mode='more-info'">
html
</xsl:if>
</xsl:template>