How to display value in xslt variable - xslt

how can I store element with attributes into xslt variable and then display element through this variable ?
Example:
<element name="value1" attribute2="value2" />
I tried something like this:
<xsl:variable name="myVariable" select="../element[#name=value1]" />
and then display:
<xsl:template match="..">
<xsl:value-of select="$myVariable" />
</xsl:template>
And I want to display element with given name with all attributes.
Thanks
Krp0

You have been missing the qoutes around value1. To access the value of the variable use xsl:copy-of which copies an element with all attributes.
<xsl:template match="/">
<xsl:variable name="myVariable" select="element[#name='value1']" />
<xsl:copy-of select="$myVariable" />
</xsl:template>

value-of creates a text node with the string value of the selected value, if you want to copy a node to the output use copy-of, e.g. <xsl:copy-of select="$myVariable"/>.

Related

My XSLT Variable doesn't load in template

So I'm trying to create a variable and then later on use this in my template.
This is the code I have now:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="items">
<xsl:call-template name="item" />
</xsl:template>
<xsl:template name="item">
<xsl:for-each select="item">
<xsl:variable name="currentItemTheme">
test variable
</xsl:variable>
<xsl:if test="title = name">
Showvariable: {$currentItemTheme} <br/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have no idea why it doesn't work. My file does output the "Showvariable: ", but it just doesn't show the test values. So the for-each loops and template aren't the problem.
Is there something I'm missing? Does someone know how I get this to work?
I think you want <xsl:value-of select="$currentItemTheme"/> instead of {$currentItemTheme}, unless you are mixing the XSLT code with some other language that provides the {$currentItemTheme} syntax.
<xsl:copy-of select="$currentItemTheme"/> is another option if you build nodes in your variable and want to output them instead of just the string value as a text node.

Attributes as arguments return empty values in xsl:contains() in XSLT

The variable $authors is declared as follows:
<listAuthor>
<author catalogNumber="26A.18.1" fullName="Pjotr Domanski"/>
<author catalogNumber="26A.19.1" fullName="Hermine Rex"/>
<author catalogNumber="26A.19.2" fullName="Christoferus Hohenzell"/>
</listAuthor>
Further, the following XML input is given:
<h1:Block Type="obj">
<h1:Field Type="5000" Value="77772001" />
<h1:Field Type="5209" Value="26A.19.1 : Lazy Tennis"/>
</h1:Block>
The expected output is:
<h1:Block Type="obj">
<h1:Field Type="5000" Value="77772001" />
<h1:Field Type="5209" Value="26A.19.1 : Lazy Tennis"/>
<h1:Field Type="9904" Value="Hermine Rex"/>
</h1:Block>
Here is a snippet of my transformation:
<xsl:template match="h1:Block">
<h1:Block Type="obj">
<xsl:copy-of select="child::*"/>
<h1:Field Type="9904">
<xsl:attribute name="Value"
select="$authors/listAuthor/author[contains (h1:Field[#Type='5209']/#Value, #catalogNumber)]/#fullName"/>
</h1:Field>
</h1:Block>
</xsl:template>
The name of the author should be put in h1:Field[#Type='9904']/#Value, when #catalogNumber in $authors/author is contained in h1:Field[#Type='5209']. Unfortunately, the attribute #Value remains empty.
I found the following workaround by introducing a variable:
<xsl:template match="h1:Block">
<h1:Block Type="obj">
<xsl:copy-of select="child::*"/>
<xsl:variable name="value5209" select="h1:Field[#Type='5209']/#Value"/>
<h1:Field Type="9904">
<xsl:attribute name="Value"
select="$authors/listAuthor/author[contains ($value5209, #catalogNumber)]/#fullName"/>
</h1:Field>
</h1:Block>
</xsl:template>
In this case, it works fine. Does anyone have any idea why it doesn't work in the first case? I use Saxon Saxon-HE 9.6.
Inside the predicate the context node is the author element which does not have a h1:Field, so change $authors/listAuthor/author[contains (h1:Field[#Type='5209']/#Value, #catalogNumber)]/#fullName to $authors/listAuthor/author[contains (current()/h1:Field[#Type='5209']/#Value, #catalogNumber)]/#fullName to select the context node of the template.

how to fetch the value of one node based on the value from another node

I want to fetch the "index" value based on what is present in the string
<sch name="main">
<norm string="back-slash"/>
<norm string="open-braces" />
<norm string="close-braces" />
</sch>
<strings name="consts">
<string name="back-slash" val="\\" index="0"/>
<string name="close-braces" val="]" index="2"/>
<string name="remove-null" val="null" index="3" />
</strings>
i tried this but it doesnt' work. Can yuou please help?
<xsl:template match="norm" >
<xsl:variable name="$nme" select="#string"/>
<xsl:value-of select="/strings/#name=$nme/#index"/>,
</xsl:template>
/strings/#name=$nme/#index
is not valid XPath. You need an attribute selector if you wish to target a node by one of its attributes.
/strings/*[#name=$nme]/#index
First of all, the name of your variable $nme is not a valid Qname.
Instead of
<xsl:variable name="$nme" select="#string"/>
you should use
<xsl:variable name="nme" select="#string"/>
try this template:
<xsl:template match="norm" >
<xsl:variable name="nme" select="#string"/>
<xsl:value-of select="../following-sibling::strings/string[#name=$nme]/#index"/>,
</xsl:template>

XSLT: Is it possible to select unique IDs into variable ordered by a criteria other than the ID itself?

Imagine the following xml
<elements>
<element>
<elementID>0x1000</elementID>
<elementSort>1</elementSort>
<elementName>Master Joda</elementName>
<modifyDate>1979-01-01</modifyDate>
</element>
<element>
<elementID>0x1000</elementID>
<elementSort>1</elementSort>
<elementName>Master Yoda</elementName>
<modifyDate>1979-01-05</modifyDate>
</element>
<element>
<elementID>0x2000</elementID>
<elementSort>2</elementSort>
<elementName>Luke Skywalker</elementName>
<modifyDate>1979-01-08</modifyDate>
</element>
</elements>
I use the following xslt to select a list of unique IDs into a variable
<xsl:variable name="ids"
select="elements/element/elementID[not(.=following::elementID)]" />
Then i let xslt build some html for each ID (the output will be a horizontal list of elements per ID)
<xsl:for-each select="$ids">
<xsl:variable name="elementID" select="." />
<div class="itemContainer clear" style="width:{$containerWidth}">
<xsl:for-each select="/elements/element[elementID=$elementID]">
<xsl:sort select="modifyDate" />
<xsl:call-template name="elementTemplate" />
</xsl:for-each>
</div>
</xsl:for-each>
The problem is: how can i sort the elements in the first level of the for-each nesting (the IDs) without having the Element by which i want to sort in the list itself (the ID list).
In practical terms: how can i sort by Jedi hierarchy (master -> pupil), if elementSort 1 means master and elementSort 2 means pupil, having multiple elements per hierarchy in each row, which are then ordered by modifyDate.
Instead of:
<xsl:for-each select="/elements/element/elementID[elementID=$elementID]">
<xsl:sort select="modifyDate" />
<xsl:call-template name="elementTemplate" />
</xsl:for-each>
BTW, the above is obviously incorrect, because an elementID element doesn't have any elementID child at all.
use:
<xsl:for-each select="/elements/element[elementID=$elementID]">
<xsl:sort select="elementSort" data-type="number" />
<xsl:sort select="modifyDate" />
<xsl:call-template name="elementTemplate" />
</xsl:for-each>
I have found a solution though it is probably not very good "xslt-design".
I store list of the unique elementSorts in an additional variable and put another for-each around the original first one. Then i combine the conditions (uniqueness and sortID) while setting the variable holding the unique element IDs.
<xsl:variable name="ids"
select="elements/element/elementID[not(.=following::elementID) and ../elementSort=$sort]" />
edit:
probably even better:
<xsl:variable name="ids"
select="elements/element[elementSort=$sort]/elementID[not(.=following::elementID)]" />
another note
if there are other nodes in the xml document that can contain elementID nodes, than you would have to specify the following:: clause as follows to avoid unwanted behaviour:
<xsl:variable name="ids"
select="elements/element[elementSort=$sort]/elementID[not(.=following::element/elementID)]" />
That way only elementIDs inside of element nodes are taken into consideration for evaluating the uniqueness, comes in handy if there were nodes that relate to the element nodes by elementID.

Is xsl:sequence always non-empty?

I don't understand output from this stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="root/sub"/>
</xsl:template>
<xsl:template match="sub">
<xsl:variable name="seq">
<xsl:sequence select="*" />
</xsl:variable>
<xsl:message>
<xsl:value-of select="#id" />
<xsl:text>: </xsl:text>
<xsl:value-of select="count($seq)" />
</xsl:message>
</xsl:template>
</xsl:stylesheet>
when applied to following XML:
<root>
<sub id="empty" />
<sub id="one"><one/></sub>
<sub id="two"><one/><one/></sub>
<sub id="three"><one/><one/><one/></sub>
</root>
Output, written by xsl:message element, is:
empty: 1
one: 1
two: 1
three: 1
I expected this one instead:
empty: 0
one: 1
two: 2
three: 3
Why does count($seq) always return 1 in this case? How would you change variable definition, so that I can later test it for emptiness? (Simple <xsl:variable name='seq' select='*' /> would return expected answer, but is not an option ... I want to change between variable in this template, and test it for emptiness later).
Let me try to answer the "why" part of your question.
If you write the following statement:
<xsl:variable name="x" select="*" />
the variable $x contains the sequence of the child nodes of the current node. There's no implicit parent node in $x, because you use select. Now consider the following:
<xsl:variable name="x">
<content />
<content />
</xsl:variable>
where variable $x contains a sequence of one node: the parent node of content. Here, count($x) will always give you 1. To get the amount of content elements, you need to count the children of the $x implicit root node: count($x/content).
As a rule of thumb, you can remember that: if xsl:variable is itself an empty element with a select attribute, the result set will be assigned to the variable. If you use xsl:variable without the select attribute, you always create an implicit parent with the variable, and the contents of the variable are the children underneath it.
The same applies for your example with xsl:sequence as a child to xsl:variable. By having a non-empty xsl:variable you create an implicit parent for the sequence, which is why you always get 1 if you count the variable itself.
If you need both: a bundle of statements inside xsl:variable, but the behavior of the select attribute, you can workaround this by using the following:
<xsl:variable name="x" select="$y/*" />
<xsl:variable name="y">
<xsl:sequence select="foo" />
</xsl:variable>
which will now yield the expected amount for count($x), but whether or not that's really beneficial or makes your code clearer is arguable.
It works as you might expect if you either change the variable declaration to:
<xsl:variable name="seq" select="*"/>
or declare the type of the variable using 'as' attribute:
<xsl:variable name="seq" as="item()*">
<xsl:sequence select="*" />
</xsl:variable>
Not specifying any type information often leeds to surprising results in XSLT 2.0. If you are using Saxon, you can output how Saxon interprets the stylesheet using the explain extension attribute:
<xsl:template match="sub" saxon:explain="yes" xmlns:saxon="http://saxon.sf.net/">
<xsl:variable name="seq">
<xsl:sequence select="*" />
</xsl:variable>
<xsl:message>
<xsl:value-of select="#id" />
<xsl:text>: </xsl:text>
<xsl:value-of select="count($seq)" />
</xsl:message>
</xsl:template>
As you can see, Saxon constructs a document-node out of the sequence:
Optimized expression tree for template at line 6 in :
let $seq[refCount=1] as document-node() :=
document-constructor
child::element()
return
message
You are selecting the sub nodes, and then counting each node - so it'll always be 1. You need to count the children, for example:
<xsl:value-of select="count($seq/*)" />
will give you the output you're expecting.