xsl:call-template: return a number rather than text to be assigned to a variable - templates

I'm having a (minor) problem here. I'm calling a named template and am assigning the outcome to a variable. So for so good, but I need the type of the processed template's return value to be integer rather than text.
I wonder if there's a way to achieve that without having to go with a temporary variable?
Here's some sample code:
<xsl:variable name="tmp">
<xsl:call-template name="mytemplate">
<xsl:with-param name="x" select="123"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="myvar" select="number($tmp)"/>
<xsl:template name="mytemplate">
<xsl:param name="x"/>
<xsl:value-of select="$x"/>
</xsl:template>
Don't mind the code as it is an oversimplification of what my template does. Notice also that I've tried to return <xsl:value-of select="number($x)"/> but to no avail.
Any help is heavily appreciated.
TIA

First, $tmp data type is Result Tree Fragment. So, besides copying, in all allowed operations with $tmp, only counts its string value.
XPath have many rules for implicit casting. In general, whenever an operator or function takes a number data type as argument, the expression will be cast to number with number() function.
Bottom line: in mostly every case you don't need that explicit casting.

As #Alejandro points out, you don't need explicit casting to number.
If you intend to use this not as a number, but to use the number-representation as an intermediate type, then you do need the cast, because the RTF that is in the $tmp variable may not be directly convertible to that type as wanted.
Example:
You need:
boolean(number($tmp))
to convert an RTF or any tree to a boolean tat can have two different values.
boolean(someNode)
is always true() -- by definition.

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?

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>

In XSLT, why do I need to do comparisons with variables instead of with attribute values (in a test expression)

I am parsing a document, with different behavior depending on whether the id attribute is an element of a collection of values ($item-ids in the code below). My question is, why do I need to assign a variable and then compare with that value, like this:
<xsl:template match="word/item">
<xsl:variable name="id" select="#abg:id"/>
<xsl:if test="$item-ids[.=$id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
It seems to be that I should be able to do it like this, though it doesn't work:
<xsl:template match="word/item">
<xsl:if test="$item-ids[.=#abg:id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
This is something I keep forgetting and having to relearn. Can anybody explain why it works this way? Thanks.
To understand XPath, you need to understand the concept of the context node. An expression like #id is selecting an attribute of the context node. And the context node changes inside square brackets.
You don't have to use a variable in this case. Here you can use:
<xsl:template match="word/item">
<xsl:if test="$item-ids[. = current()/#abg:id]">
<xsl:message>It matches!</xsl:message>
</xsl:if>
</xsl:template>
The reason you can't just use $item-ids[. = #abg:id] is that inside the [], you are in the context of whatever is right before the [] (in this case $item-ids), so #abg:id would be treated as $item-ids/#abg:id, which isn't what you want.
current() refers to the current context outside of the <xsl:if> so current()/#abg:id should reflect you the value you want.
I think it's because the line
<xsl:if test="$item-ids[.=#abg:id]">
compares the value of $item-ids to the string '#abg:id' - you need to compare it to the value of #abg:id which is why you need to select that value into the $id variable for the test to work.
Does that help at all?
Edit: I've misunderstood the issue - the other answers are better than mine.

How to know variable has value or not in XSLT

I am creating XSLT file.
I have one variable which take value from XML file.But it may happen that there is no reference in xml for the value and at that time XSL variable will return False/None(don't know).I want keep condition like,If there is no value for the variable use the default one.
How to do that ?
With the few details given in the question, the simplest test you can do is:
<xsl:if test="$var">
...
</xsl:if>
Or you might use xsl:choose if you want to provide output for the else-case:
<xsl:choose>
<xsl:when test="not($var)"> <!-- parameter has not been supplied -->
</xsl:when>
<xsl:otherwise> <!--parameter has been supplied --> </xsl:otherwise>
</xsl:choose>
The second example will also handle the case correctly that the variable or parameter has not been supplied with an actual value, i.e. it equals the empty string. This works because not('') returns true.
You haven't explained what you mean by "has no value". Here is a generic solution:
not($v) and not(string($v))
This expression evaluates to true() iff $v "has no value".
Both conditions need to be met, because a string $v defined as '0' has a value, but not($v) is true().
In XSLT 1.0 using a default can be achieved in different ways if the "value" is a node-set or if the value is a scalar (such as a string, a number or a boolean).
#Alejandro provided one way to get a default value if a variable that is supposed to contain a node-set is empty.
If the variable is supposed to contain a scalar, then the following expression returns its value (if it has a value) or (otherwise) the desired default:
concat($v, substring($default, 1 div (not($v) and not(string($v)))))
You can use string-length to check, if a variable called $reference for example contains anything.
<xsl:choose>
<xsl:when test="string-length($reference) > 0">
<xsl:value-of select="$reference" />
</xsl:when>
<xsl:otherwise>
<xsl:text>some default value</xsl:text>
</xsl:otherwise>
</xsl:choose>
If necessary use normalize-space, too.
First, all variables has values, because XSLT belongs to declarative paradigm: there is no asignation instruction, but when you declare the variable you are also declaring the expression for its value relationship.
If this value it's a node set data type (that looks from your question), then you should test for an empty node set in case nothing was selected. The efective boolean value for an empty node set is false. So, as #0xA3 has answered: test="$node-set".
You wrote:
If there is no value for the variable
use the default one. How to do that ?
Well, that depends on what kind of data type you are looking for.
Suppose the node set data type: if you want $node-set-1 value or $node-set-2 if $node-set-1 is empty, then use:
$node-set-1|$node-set-2[not($node-set-1)]
I tried a lot of solution from SO, my last solution was taken from #dimitre-novatchev, but that one also not working every time. Recently I found one more solution from random google search, thought to share with the community.
In order to check empty variable value, we can declare an empty variable and compare its value against test condition. Here is code snippet:
<xsl:variable name="empty_string"/>
<xsl:if test="testVariableValue != $empty_string">
...
</xsl:if>
Here testVariableValue hold the value of new variable to be tested for empty
scenario.
Hope it would help to test empty variable state.

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