How to use xsl:number count as condition in xsl:if test "condition"? - xslt

Within an xsl:for-each select loop, I have <xsl:number count="//headline"/> that correctly gives me the node #; Now I want to use that number in an xsl:if test block, but I cannot get the test expression right, msxml4.dll keeps kicking back errors. Am using xsl 1.0 (and stuck with it for now)
So, in <xsl:if test="expression">...output if the expression is true..</xsl:if>
I want the test expression to essentially be like this (so I can do something specific for Node #4, in this example):
<xsl:number count="//headline"/> = 4
This is what I have that does not work:
<xsl:if test="<xsl:number count="//headline"/> = 4">
Thanks in advance for any insights,
George

As #michael.hor257k explains, the general approach is to put the xsl:number call inside an xsl:variable (or in XSLT 2.0, inside an xsl:function). Sometimes though it's more convenient to abandon xsl:number:
<xsl:if test="count(preceding::headline) = 3">...</xsl:if>
If it's a big document then both xsl:number and preceding::headline are potentially expensive when executed inside a loop, and if this is a concern then you should compare them under your chosen XSLT processor: one may be optimized better than the other.
Come to think of it, your use of xsl:number looks odd to me. The count attribute is a pattern, and the pattern //headline matches exactly the same nodes as the pattern headline. As a result I misread your call on xsl:number as counting all the headlines in the document, whereas it actually only counts the preceding-sibling headlines. I wonder if that is what you intended?

If (!) I understand correctly, you want to do something like:
<xsl:variable name="n">
<xsl:number count="headline"/>
</xsl:variable>
<xsl:value-of select="$n"/>
<xsl:if test="$n = 4">
<!-- do something -->
</xsl:if>

Related

how to do an if like statement or equivalent in XSLT

Is it possible to write out xml based on an "if" "Like" statement or the equivalent of in xslt?
I have an element named "cust_code"
If the element starts with a "HE" then I want to write it out, otherwise jump to the next.
Is it possible?
If statements exist in XSLT.
<xsl:if test="...">
...
</xsl:if>
But this is a simple if, with no alternative.
If you want an equivalent to if ... else ... or switch ... case ..., you need to use the following:
<xsl:choose>
<xsl:when test="...">
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
You can have as many when cases as necessary.
Links: w3school - if and w3school - choose.
As to having an element starting with a specific string, look at the function starts-with. You can find a good example in this SO answer (just omit the not from the main answer, as their tests was to find strings not starting with a particular string). You can also look at this answer for more information.

when to use xsl:if and when to use xsl:choose/xsl:when in XSLT

I know this may seem like a dumb/newbie question, but I'm fairly new to XSLT (though I'm starting to come around to it and see it's capabilities).
When is it appropriate to use xsl:if and when is it appropriate to use xsl:choose/xsl:when?
I have been using choose/when/otherwise when I want to have an "else" option. Is this correct?
For instance I have some places where I'm doing:
<xsl:choose>
<xsl:when test="count(entry) != 0">
put positive outcome here
</xsl:when>
<xsl:otherwise>
put something else here
</xsl:otherwise>
</xsl:choose>
would xsl:if be better?
Thanks for the input.
Use xsl:if for simple cases where you just want to test if an expression is true. (Note that there is no corresponding xsl:else.)
<xsl:if test="expression">
output if the expression is true
</xsl:if>
Use xsl:choose for cases where you have some alternate output when the expressions is false.
<xsl:choose>
<xsl:when test="expression">
output if the expression is true
</xsl:when>
<xsl:otherwise>
output if the expression is false
</xsl:otherwise>
</xsl:choose>
would xsl:if be better?
Not really - unless you want to have two xsl:ifs and ensure that their conditions are mutually exclusive.
an xsl:choose will always select exactly one of the available xsl:when or xsl:otherwise
The <xsl:when> children of the <xsl:choose> element are tested, in order from top to bottom, until a test attribute on one of these elements accurately describes conditions in the source data, or until an <xsl:otherwise> element is reached. Once an <xsl:when> or <xsl:otherwise> element is chosen, the <xsl:choose> block is exited. No explicit break or exit statement is required.
It's remarkably similar to a switch statement from C inspired languages (C, C++, Java, C#) or Select...Case from Visual Basic
The xsl:if doesn't have the equivalent from such languages of an else clause, so I'd only recommend it if you want to either do "something" or not (I.e. in the not case, you don't want to specify an alternative)

XSLT, how to use "xsl:value-of" inside "xsl:if"?

I am new to xslt and I am wondering whether it is possible to compare the value of "#userNameKey" and the value of
<xsl:value-of select="./text()"/> in example below?
<xsl:if test="#userNameKey='??????'">
<xsl:attribute name="selected">true</xsl:attribute>
</xsl:if>
<xsl:value-of select="./text()"/>
Basically, I just want to replace the questionmarks with the following fragment: <xsl:value-of select="./text()"/> but there is an issue with the double quotes. Should I use escape characters (if yes, what are they?) or there is a better solution?
If you specifically want to compare against the value of the first text node child of the current element (which is what <xsl:value-of select="./text()"/> gives you), then use
<xsl:if test="#userNameKey=string(text())">
At first sight
<xsl:if test="#userNameKey=text()">
may seem more obvious, but this is subtly different, returning true if the userNameKey matches any one of the text node children of the current element (not necessarily the first one).
But if (as I suspect you really mean) you want to compare the userNameKey against the complete string value of the element even if that consists of more than one text node, then use
<xsl:if test="#userNameKey=.">
Remember that text() is a node set containing all the text node children of the context node, and if you're not sure you need to use it (e.g. when you want to process each separate text node individually) then you probably don't.
You should be able to do just this...
<xsl:if test="#userNameKey=./text()">
<xsl:attribute name="selected">true</xsl:attribute>
</xsl:if>
In fact, the ./ is not needed here, so you can just do this
<xsl:if test="#userNameKey=text()">
<xsl:attribute name="selected">true</xsl:attribute>
</xsl:if>

xsl: how to use parameter inside "match"?

My xsl has a parameter
<xsl:param name="halfPath" select="'halfPath'"/>
I want to use it inside match
<xsl:template match="Element[#at1='value1' and not(#at2='{$halfPath}/another/half/of/the/path')]"/>
But this doesn't work. I guess a can not use parameters inside ''. How to fix/workaround that?
The XSLT 1.0 W3C Specification forbids referencing variables/parameters inside a match pattern.:
"It is an error for the value of the
match attribute to contain a
VariableReference"
There is no such limitation in XSLT 2.0, so use XSLT 2.0.
If due to unsurmountable reasons using XSLT2.0 isn't possible, put the complete body of the <xsl:template> instruction inside an <xsl:if> where the test in conjunction with the match pattern is equivalent to the XSLT 2.0 match pattern that contains the variable/parameter reference(s).
In a more complicated case where you have more than one template matching the same kind of node but with different predicates that reference variables/parameters, then a wrapping <xsl:choose> will need to be used instead of a wrapping <xsl:if>.
Well, you could use a conditional instruction inside the template:
<xsl:template match="Element[#at1='value1']">
<xsl:if test="not(#at2=concat($halfPath,'/another/half/of/the/path'))">
.. do something
</xsl:if>
</xsl:template>
You just need to be aware that this template will handle all elements that satisfy the first condition. If you have a different template that handles elements that match the first, but not the second, then use an <xsl:choose>, and put the other template's body in the <xsl:otherwise> block.
Or, XSLT2 can handle it as is if you can switch to an XSLT2 processor.
This topic had the answer to my question, but the proposed solution by Flynn1179 was not quite correct for me (YMMV). So try it the way it is suggested by people more expert than me, but if it doesn't work for you, consider how I solved it. I am using xsltproc that only handles XSL version 1.0.
I needed to match <leadTime hour="0024">, but use a param: <xsl:param name="hour">0024</xsl:param>. I found that:
<xsl:if test="#hour='{$hour}'"> did not work, despite statements here and elsewhere that this is the required syntax for XSL v.1.0.
Instead, the simpler <xsl:if test="#hour=$hour"> did the job.
One other point: it is suggested above by Dimitre that you put template inside if statement. xsltproc complained about this: instead I put the if statement inside the template:
<xsl:template match="leadTime">
<xsl:if test="#hour=$leadhour">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
In XSLT 2.0 you can refer to global variables within a match pattern, but the syntax is simpler than your guess:
<xsl:template match="Element[#at1='value1' and
not(#at2=$halfPath/another/half/of/the/path)]"/>
rather than
<xsl:template match="Element[#at1='value1' and
not(#at2='{$halfPath}/another/half/of/the/path')]"/>
Also, the semantics are not what you appear to be expecting: a variable referenced on the lhs of "/" must contain a node-set, not a fragment of an XPath expression.

xsl strange behaviour using variables

I've stored a file's tree into $onto
<xsl:variable name="onto" select="document('file.xml')"/>
In some places I can use this variable as espected:
<xsl:copy-of select="$onto/rdf:RDF"/>
But I'm having trouble in other places, strange chars are written on output:
<xsl:element name="autor">
<xsl:attribute name="rdf:resource">
<xsl:text>#</xsl:text> <xsl:value-of select="$onto"/>
</xsl:attribute>
</xsl:element>
This is the beginig of the output I've got:
<autor rdf:resource="#
What I'm missing? What's wrong?
If that's to much for an attribute, what can I do?
Thank you
When <xsl:value-of> is applied to a tree fragment, it takes the text content of that tree. In your case, it looks like your XML file doesn't contain any text (other than whitespace) which isn't in an attribute value. I suspect that you mean to select the value of a particular attribute node within the document, e.g.:
<xsl:value-of select="$onto//foo/#bar"/>
(Without knowing the structure of your XML and what you're trying to select, I don't know what the real path would be.)