How check which variable is greater in xsl? - xslt

<xsl:variable name="a">20</xsl:variable>
<xsl:variable name="b">10</xsl:variable>
<xsl:if test="($a) > ($b)">
------
</xsl:if>
I getting error in the if condion..

Try the following :
<xsl:if test="$a > $b">
Try using the character entities for > (>) and < (<) operators in expressions, otherwise some parsers think you are closing the tag early, or opening another.

The example you posted should work. However, you should not that in your case both variables are of type string which could give surprising results where their length differs. The behaviour of the comparison operator on different datatypes is specified in the xpath spec on booleans.
To avoid this you could declare the variables using the select attribute or manually convert them to number for the comparison:
<xsl:variable name="a" select="20"/>
<xsl:variable name="b" select="10"/>
...
<xsl:if test="number($a) > number($b)">
</xsl:if>

Related

XSLT/Xpath: How to find out that something is or isn´t in key (with if)?

I have problem with my code in XSLT with if. I am using key function and there I find out if something is in the key or not.
<xsl:key name="hlp" match="help" use="#id" />
...
<xsl:if test="key('hlp', #some_id) !=''">
...
</xsl:if>
That is correct it gives me what I want but how I can make opposite condition that #some_id isn´t in key hlp... I mean:
<xsl:if test="key('hlp', #some_id) <!--is equal--> ''">
...
</xsl:if>
Is there something like that in XSLT/XPath?
When you call key('x', 'y'), the result is the set of nodes in which the key is equal to 'y'. You can test whether a node-set is empty using the empty() function (in XSLT 2.0) or the not() function in XSLT 1.0:
<xsl:if test="not(key('x', 'y'))" version="1.0">...</xsl:if>
<xsl:if test="empty(key('x', 'y'))" version="2.0">...</xsl:if>
or for the inverse test (to test if something was found):
<xsl:if test="key('x', 'y')" version="1.0">...</xsl:if>
<xsl:if test="exists(key('x', 'y'))" version="2.0">...</xsl:if>
Testing by comparing the result against a string is wrong. The tests key('x','y')='' and key('x','y')!='' will both return false if the result of the key() function is an empty node-set; conversely, if the key() function selects two nodes, one with content and the other without, then both tests will return true.
<xsl:key name="hlp" match="help" use="#id" />
...
<xsl:if test="key('hlp', #some_id) !=''">
...
</xsl:if>
As already pointed by Michael Kay, avoid using the != operator unless truly knowing what it does.
This aside (and the fact that the key() function returns a node-set), it is more in the spirit of XSLT to write the above as:
<xsl:apply-templates select="key('hlp', #some_id)"/>
but how I can make opposite condition that #some_id isn´t in key
hlp... I mean:
<xsl:if test="key('hlp', #some_id) <!--is equal--> ''">
...
</xsl:if> ```
Is there something like that in XSLT/XPath?
Again, in the spirit of XSLT I recommend using code like this:
<xsl:apply-templates select="/*[not(key('hlp', #some_id))]" mode="not-found"/>
In the select expression above one can substitute /* with any existing node in the document -- if this really matters.

Looping through an xpath getting the right iteration each time in xslt 2

I have a condition where I need to loop atleast once and so I have the following xsl code. However, this doesnt work as it always gets the last iterations value. How can I tweak this so it gets the right iteration on each loop?
<xsl:variable name='count0'>
<xsl:choose>
<xsl:when test='count($_BoolCheck/BoolCheck[1]/CheckBoolType) = 0'>
<xsl:value-of select="1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='count($_BoolCheck/BoolCheck[1]/CheckBoolType)'/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:for-each select="1 to $count0">
<xsl:variable name='_LoopVar_2_0' select='$_BoolCheck/BoolCheck[1]/CheckBoolType[position()=$count0]'/>
<e>
<xsl:attribute name="n">ValueIsTrue</xsl:attribute>
<xsl:attribute name="m">f</xsl:attribute>
<xsl:attribute name="d">f</xsl:attribute>
<xsl:if test="(ctvf:isTrue($_LoopVar_2_0/CheckBoolType[1]))">
<xsl:value-of select=""Value True""/>
</xsl:if>
</e>
</xsl:for-each>
The xml file is as follows:
<BoolCheck>
<CheckBoolType>true</CheckBoolType>
<CheckBoolType>false</CheckBoolType>
<CheckBoolType>1</CheckBoolType>
<CheckBoolType>0</CheckBoolType>
<CheckBoolType>True</CheckBoolType>
<CheckBoolType>False</CheckBoolType>
<CheckBoolType>TRUE</CheckBoolType>
<CheckBoolType>FALSE</CheckBoolType>
</BoolCheck>
In this case I need to iterate through each iteration of CheckBoolType and produce a corresponding number of values. However, in the above example if there were no CheckBoolType iterations I would still like the iterations to enter the for-each loop atleast once. i hope that clarifies it a little more.
First observation: your declaration of $count0 can be replaced by
<xsl:variable name="temp" select="count($_BoolCheck/BoolCheck[1]/CheckBoolType)"/>
<xsl:variable name="count0" select="if ($temp=0) then 1 else $temp"/>
(Sorry if that seems irrelevant, but my first step in debugging code is always to simplify it. It makes the bugs much easier to find).
When you do this you can safely replace the predicate [position()=$count0] by [$count0], because $count0 is now an integer rather than a document node. (Even better, declare it as an integer using as='xs:integer' on the xsl:variable declaration.)
But hang on, $count0 is the number of elements being processed, so CheckBoolType[$count] will always select the last one. That's surely not what you want.
This brings us to another bug in your code. The value of the variable $_LoopVar_2_0 is an element node named CheckBoolType. The expression $_LoopVar_2_0/CheckBoolType[1] is looking for children of this element that are also named CheckBoolType. There are no such children, so the expression selects an empty sequence, so the boolean test is always false.
At this stage I would like to show you some correct code to achieve your desired output. Unfortunately you haven't shown us the desired output. I can't reverse engineer the requirement from (a) your incorrect code, and (b) your prose description of the algorithm you are trying to implement.

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?

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.

XSLT Date Comparisons

<xsl:variable name="date1" select="2011-10-05"/>
<xsl:variable name="date2" select="2011-10-05"/>
<xsl:variable name="date3" select="2011-10-06"/>
<xsl:if test="$date2 = $date1 or $date2 < $date1">
..do something
</xsl:if>
<xsl:if test="$date3 = $date1 or $date3 > $date1">
.. do something
</xsl:if>
Both should evaluate true, but the second if doesn't. For the life of me I can't comprehended why!
In the actual transform the dates themselves are being drawn from an XML document but debugging through VS2010 i can see the values are as above.
Must be something fairly fundamental i'm doing wrong - any help would be brilliant!
I tried this in Oxygen/XML... select="2011-10-05 is being interpreted as an arithmetic expression, giving the value 1996 (2011 minus 10 minus 5) and "2011-10-06" is intrepreted as 1995.
What you want is
<xsl:variable name="date1" select="'2011-10-05'"/>
<xsl:variable name="date2" select="'2011-10-05'"/>
<xsl:variable name="date3" select="'2011-10-06'"/>
Note the extra single quotes.
From the XSLT 1.0 Specification:
If the variable-binding element has a select attribute, then the value
of the attribute must be an expression and the value of the variable
is the object that results from evaluating the expression.