How to set XSLT variable to contain exact xml data - xslt

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)]"/>

Related

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?

Constructing, not selecting, XSL node set variable

I wish to construct an XSL node set variable using a contained for-each loop. It is important that the constructed node set is the original (a selected) node set, not a copy.
Here is a much simplified version of my problem (which could of course be solved with a select, but that's not the point of the question). I've used the <name> node to test that the constructed node set variable is in fact in the original tree and not a copy.
XSL version 1.0, processor is msxsl.
Non-working XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:variable name="entries">
<xsl:for-each select="//entry">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="entryNodes" select="msxsl:node-set($entries)"/>
<xsl:for-each select="$entryNodes">
<xsl:value-of select="/root/name"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML input:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>X</name>
<entry>1</entry>
<entry>2</entry>
</root>
Wanted output:
X1X2
Actual output:
12
Of course the (or a) problem is the copy-of, but I can't work out a way around this.
There isn't a "way around it" in XSLT 1.0 - it's exactly how this is supposed to work. When you have a variable that is declared with content rather than with a select then that content is a result tree fragment consisting of newly-created nodes (even if those nodes are a copy of nodes from the original tree). If you want to refer to the original nodes attached to the original tree then you must declare the variable using select. A better question would be to detail the actual problem and ask how you could write a suitable select expression to find the nodes you want without needing to use for-each - most uses of xsl:if or xsl:choose can be replaced with suitably constructed predicates, maybe involving judicious use of xsl:key, etc.
In XSLT 2.0 it's much more flexible. There's no distinction between node sets and result tree fragments, and the content of an xsl:variable is treated as a generic "sequence constructor" which can give you new nodes if you construct or copy them:
<xsl:variable name="example" as="node()*">
<xsl:copy-of select="//entry" />
</xsl:variable>
or the original nodes if you use xsl:sequence:
<xsl:variable name="example" as="node()*">
<xsl:sequence select="//entry" />
</xsl:variable>
I wish to construct an XSL node set variable using a contained
for-each loop.
I have no idea what that means.
It is important that the constructed node set is the original (a
selected) node set, not a copy.
This part I think I understand a little better. It seems you need to replace:
<xsl:variable name="entries">
<xsl:for-each select="//entry">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
with:
<xsl:variable name="entries" select="//entry"/>
or, preferably:
<xsl:variable name="entries" select="root/entry"/>
The resulting variable is a node-set of the original entry nodes, so you can do simply:
<xsl:for-each select="$entries">
<xsl:value-of select="/root/name"/>
<xsl:value-of select="."/>
</xsl:for-each>
to get your expected result.
Of course, you could do the same thing by operating directly on the original nodes, in their original context - without requiring the variable.
In response to the comments you've made:
We obviously need a better example here, but I think I am getting a vague idea of where you want to go with this. But there are a few things you must understand first:
1.
In order to construct a variable which contains a node-set of nodes in their original context, you must use select. This does not place any limits whatsoever on what you can select. You can do your selection all at once, or in stages, or even in a loop (here I mean a real loop). You can combine the intermediate selections you have made in any way sets can be combined: union, intersection, or difference. But you must use select in all these steps, otherwise you will end up with a set of new nodes, no longer having the context they did in the source tree.
IOW, the only difference between using copy and select is that the former creates new nodes, which is precisely what you wish to avoid.
2.
xsl:for-each is not a loop. It has no hierarchy or chronology. All the nodes are processed in parallel, and there is no way to use the result of previous iteration in the current one - because no iteration is "previous" to another.
If you try to use xsl:for-each in order to add each of n processed nodes to a pre-existing node-set, you will end up with n results, each containing the pre-existing node-set joined with one of the processed nodes.
3.
I think you'll find the XPath language is quite powerful, and allows you to select the nodes you want without having to go through the complicated loops you hint at.
It might help if you showed us a problem that can't be trivially solved in XSLT 1.0. You can't solve your problem the way you are asking for: there is no equivalent of xsl:sequence in XSLT 1.0. But the problem you have shown us can be solved without such a construct. So please explain why you need what you are asking for.

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>

Building an xslt variable with elements from multiple files

I'm no XSLT guru and couldn't find a similar example online. I'd like to assemble a list of apps from multiple files into a single var that can be used for searching.
Basically when I replace the original variable decleration with the new one, XSLT doesn't like it. I did output the variable contents to a file and they are identical in formatting of the XML elements so it must be failing around some metadata linked to the variable somewhere.
XML element format in all the files
<include-application name="appname" type="blah"/>
Orignal variable
<xsl:variable name="applications" select="board/packaging/*/include-application"/>
New variable definition
<xsl:variable name="applications">
<xsl:copy-of select="board/packaging/*/include-application"/>
<xsl:for-each select="board/packaging/applications/include">
<xsl:variable name="appset" as="xs:string" select="#name"/>
<xsl:variable name="includefile" as="xs:string" select="concat('../share/appsets/', $appset, '.xml')"/>
<xsl:copy-of select="document($includefile)/applications/include-application"/>
</xsl:for-each>
</xsl:variable>
Then when I try to access the elements to pick something, it fails with the new variable definition (line 39 is the first one in block below).
<xsl:variable name="type" select="$applications[#name = $appname]/#type"/>
<xsl:variable name="appid" select="$app-names/application-package-name[#name = $appname]/appid[#type = $type]/#value"/>
XPath error : Invalid type
runtime error: file xslt/blah.xslt line 39 element variable
Failed to evaluate the expression of variable 'type'.
Thanks
David
Since you are using XSLT 2.0, as evidenced by your use of as=, you need to declare the memory organization of your variable so as to act on it later. I think all you need to do is simply change the first line of your variable to be:
<xsl:variable name="applications" as="element(include-application)*">
...
That will tell the processor not to build a tree, but rather, to build a node set.
There is helpful information and a diagram on page 223 of my XSLT book that is available for free download on a "try and buy" basis at http://www.CraneSoftwrights.com/training/#ptux ... if you decide not to pay for the book, please delete the copy that you download for free.
The problem should be that the variable contains a XML fragment instead than a node-set (see this for example) - different XSLT processor have different extension functions to do the conversion - for example using a Microsoft processor you would do:
<xsl:variable name="type" select="msxsl:node-set($applications)[#name = $appname]/#type"/>
other processors have different functions (or the same function in a different namespace)
Using a combination of the above answers and looking into it more deeply, I used the exslt extension for converting to a node set and it works flawlessly now.
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
...
<xsl:variable name="applications" as="element(include-application)*">
...
<xsl:variable name="type" select="exslt:node-set($applications)/include-application[#name = $appname]/#type"/>

XSLT xsl:sequence. What is it good for..?

I know the following question is a little bit of beginners but I need your help to understand a basic concept.
I would like to say first that I'm a XSLT programmer for 3 years and yet there are some new and quite basics things I've been learning here I never knew (In my job anyone learns how to program alone, there is no course involved).
My question is:
What is the usage of xsl:sequence?
I have been using xsl:copy-of in order to copy node as is, xsl:apply-templates in order to modifiy nodes I selected and value-of for simple text.
I never had the necessity using xsl:sequence. I would appreciate if someone can show me an example of xsl:sequence usage which is preferred or cannot be achieved without the ones I noted above.
One more thing, I have read about the xsl:sequence definition of course, but I couldn't infer how it is useful.
<xsl:sequence> on an atomic value (or sequence of atomic values) is the same as <xsl:copy-of> both just return a copy of their input. The difference comes when you consider nodes.
If $n is a single element node, eg as defined by something like
<xsl:variable name="n" select="/html"/>
Then
<xsl:copy-of select="$n"/>
Returns a copy of the node, it has the same name and child structure but it is a new node with a new identity (and no parent).
<xsl:sequence select="$n"/>
Returns the node $n, The node returned has the same parent as $n and is equal to it by the is Xpath operator.
The difference is almost entirely masked in traditional (XSLT 1 style) template usage as you never get access to the result of either operation the result of the constructor is implicitly copied to the output tree so the fact that xsl:sequence doesn't make a copy is masked.
<xsl:template match="a">
<x>
<xsl:sequence select="$n"/>
</x>
</xsl:template>
is the same as
<xsl:template match="a">
<x>
<xsl:copy-of select="$n"/>
</x>
</xsl:template>
Both make a new element node and copy the result of the content as children of the new node x.
However the difference is quickly seen if you use functions.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="data:,f">
<xsl:variable name="s">
<x>hello</x>
</xsl:variable>
<xsl:template name="main">
::
:: <xsl:value-of select="$s/x is f:s($s/x)"/>
:: <xsl:value-of select="$s/x is f:c($s/x)"/>
::
:: <xsl:value-of select="count(f:s($s/x)/..)"/>
:: <xsl:value-of select="count(f:c($s/x)/..)"/>
::
</xsl:template>
<xsl:function name="f:s">
<xsl:param name="x"/>
<xsl:sequence select="$x"/>
</xsl:function>
<xsl:function name="f:c">
<xsl:param name="x"/>
<xsl:copy-of select="$x"/>
</xsl:function>
</xsl:stylesheet>
Produces
$ saxon9 -it main seq.xsl
<?xml version="1.0" encoding="UTF-8"?>
::
:: true
:: false
::
:: 1
:: 0
::
Here the results of xsl:sequence and xsl:copy-of are radically different.
The most common use case for xsl:sequence is to return a result from xsl:function.
<xsl:function name="f:get-customers">
<xsl:sequence select="$input-doc//customer"/>
</xsl:function>
But it can also be handy in other contexts, for example
<xsl:variable name="x" as="element()*">
<xsl:choose>
<xsl:when test="$something">
<xsl:sequence select="//customer"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="//supplier"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
The key thing here is that it returns references to the original nodes, it doesn't make new copies.
Well to return a value of a certain type you use xsl:sequence as xsl:value-of despite its name always creates a text node (since XSLT 1.0).
So in a function body you use
<xsl:sequence select="42"/>
to return an xs:integer value, you would use
<xsl:sequence select="'foo'"/>
to return an xs:string value and
<xsl:sequence select="xs:date('2013-01-16')"/>
to return an xs:date value and so on. Of course you can also return sequences with e.g. <xsl:sequence select="1, 2, 3"/>.
You wouldn't want to create a text node or even an element node in these cases in my view as it is inefficient.
So that is my take, with the new schema based type system of XSLT and XPath 2.0 a way is needed to return or pass around values of these types and a new construct was needed.
[edit]Michael Kay says in his "XSLT 2.0 and XPath 2.0 programmer's reference" about xsl:sequence: "This innocent looking instruction introduced in XSLT 2.0 has far reaching effects on the capability of the XSLT language, because it means that XSLT instructions and sequence constructors (and hence functions and templates) become capable of returning any value allowed by the XPath data model. Without it, XSLT instructions could only be used to create new nodes in a result tree, but with it, they can also return atomic values and references to existing nodes.".
Another use is to create a tag only if it has a child. An example is required :
<a>
<b>node b</b>
<c>node c</c>
</a>
Somewhere in your XSLT :
<xsl:variable name="foo">
<xsl:if select="b"><d>Got a "b" node</d></xsl:if>
<xsl:if select="c"><d>Got a "c" node</d></xsl:if>
</xsl:variable>
<xsl:if test="$foo/node()">
<wrapper><xsl:sequence select="$foo"/></wrapper>
</xsl:if>
You may see the demo here : http://xsltransform.net/eiZQaFz
It is way better than testing each tag like this :
<xsl:if test="a|b">...</xsl:if>
Because you would end up editing it in two places. Also the processing speed would depend on which tags are in your imput. If it is the last one from your test, the engine will test the presence of everyone before. As $foo/node() is an idioms for "is there a child element ?", the engine can optimize it. Doing so, you ease the life of everyone.