xsl:if at least one child node exists - xslt

<node>
<node1><node11/></node1>
<node2/>
</node>
I want my XSLT to check
<xsl:if test="If at least 1 child node exists">
Only node1 can pass the if condition
</xsl:if>
Thanks for any reply.

Firstly, be careful with your terminology here. Do you mean "node" or do you mean "element". A node can be an element, comment, text or processing-instruction.
Anyway, if you do mean element here, to check at least one child element exists, you can just do this (assuming you are positioned on the node element in this case.
<xsl:if test="*">
Your comment suggests only "node1" can pass the if condition, so to check the existence of a specific element, do this
<xsl:if test="node1">

In the context of the node you are testing, this should work to test whether a node has child elements:
<xsl:if test="*">
Only node1 can pass the if condition
</xsl:if>
If you actually meant nodes (which would include text nodes), then this would work to include text nodes:
<xsl:if test="node()">
Only node1 can pass the if condition
</xsl:if>
But <node> would also pass this test (<node2> wouldn't). I assumed you were only speaking in the context of <node>'s child nodes, but perhaps not?

Expressions that match a node are truthy, whilst expressions that don't match anything are falsy, so:
<xsl:if test="node()">
...
</xsl:if>
However, your question and the implied condition "Only node1 can pass the if condition" are at odds with the example. Both node and node1 have child nodes, so both would pass this if condition.
To limit it strictly to node1, you have to either ensure that the template context is appropriate, or check that the node in question is not the documentElement.

The wording of the question is unclear but I think you just want to process child nodes that have themselves got children (ie grandchildren of the current node)
<xsl:template match="node">
do stuff for node
<xsl:apply-templates select="*[*]"/>
</xsl:template>
will just apply templates to node1 as it has a child node, it will not apply templates to node2.

Related

What does this do? <xsl:apply-templates select="."/> and <xsl:apply-templates select="*|#*"/>

I am very new to XSL and I am confused about what the select inside the following pieces of code will select.
<xsl:apply-templates select="."/>
<xsl:apply-templates select="*|#*"/>
Any ideas?
Thanks
Check out the Abbreviated Syntax section of XPath 2.0.
In the <xsl:apply-templates select="."/> example, . evaluates to the context item. In most cases, this is the same as the node currently being processed. So this example will select the context node.
In the <xsl:apply-templates select="*|#*"/> example, * will select all the child elements of the context node. #* will select all the attributes of the context node. | is the union operator. So this example will select all the child elements of the context node and also all the attributes of the context node.
<xsl:apply-templates select="."/> is frequently used to apply further processing to the context node.
<xsl:apply-templates select="*|#*"/> is frequently used to process all the child elements of the current node and its attributes. It’s often used when you’re done handling an element and want to hand off its child elements/attributes to any other templates that apply.
<xsl:apply-templates select="."/>
processes the content of the current node! the dot . indicates content.. if the current node doesn't have childnodes but has data instead (ex: <foo>Sample Data</foo>) then the parser processes the data Sample Data
<xsl:apply-templates select="#*|*"/>
processes the attribute and child nodes or the data under the current node.. difference is .. this one takes care of all the attribute of the context node..
I use the word process instead of copy, because .. apply-template unlike copy-of and value-of evaluates other templates, for example along with above code if I have one more template like below:
<xsl:template match="text()[.='Sample Data']"/>
then it will drop the text from your output XML. Where as copy-of select="node_name" and value-of select="node-name" copy the data despite of being this template in our XSL file..

Why do I have to assign this node to a variable in my stylesheet?

I'm working on an XSLT stylesheet. I have a a node (node A) with a bunch of children, and I'm looping through another node's (node B) children. I'm trying to do something each time a child of node B is also a child of node A, so I have this code:
<xsl:if test="$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]">
But that doesn't work (the test fails; the expression returns false) even though the left and right side of the expression, when evaluated separately, are equal.
But when I do this:
<xsl:variable name="curbin" select="/root/Line[1]/Element[6]/text()"/>
<xsl:if test="$prodbins/bin[./text()=$curbin]">
The expression evaluates to true. Why do I have to use the $curbin variable to get the result I'm expecting?
Can you try <xsl:if test="$prodbins/bin[./text()=current()/root/Line[1]/Element[6]/text()]"> (notice current() function). The reason why it does not work in your original expression is that because you query a variable and / looks up the root node of the content of the variable and not the source document you are transforming. current() should return the context element for the template you are in.
My guess is that $prodbins/bin is a node(-set) belonging to a different document than the document that contains the nodes that are being compared to.
In the expression:
$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]
the subexpression
/root/Line[1]/Element[6]/text()
selects from the same document as the one that is the document from which the $prodbins/bin nodes are selected.
One way to specify successfully the wanted comparisson is:
<xsl:variable name="vDoc" select="/"/>
<xsl:if test="$prodbins/bin[./text()=$vDoc/root/Line[1]/Element[6]/text()]">

identifying the null element in XSLT which has no value even in child elements

checking for an Null element
<image><a><img src="abcd"/></a></image>
XSLT template:
<xsl:if test="image!=''">
IMAGE HAS TEXT OR INNER ELEMENT
</xsl:if>
I am getting blank output though the "image" element has child elements. and Ideally it is not null.
I have to check the condition that it should have value or the child elements. The child elements can be empty.
How to rectify this.
Thank you
Use:
image[string() or node()]
This evaluates to true() only if there is at least one image child of the current node, such that its string value is non-empty, or it has children (or both).
This can be simplified just to:
image[node()]
taking into account that in order to have string value, an element must have a text node descendant in its sub-tree.
If you want the string value of image (if any) to be not all-whitespace, modify the first of the above XPath expressions to:
image[normalize-space() or node()]
Use <xsl:if test="image/node()">...</xsl:if> to check whether the image element has any kind of child node or <xsl:if test="image/*">...</xsl:if> to test whether the image element has at least one child element.
You can use this to check for any child nodes (text, elements etc)
<xsl:template match="image">
<xsl:if test="node()">
IMAGE HAS TEXT OR INNER ELEMENT
</xsl:if>
</xsl:template>
Or you can be more specific:
<xsl:template match="image">
<xsl:if test="a | text()">
IMAGE HAS TEXT OR INNER ELEMENT
</xsl:if>
</xsl:template>

Go up one node and see if the parent node equates a particular node in XSLT

I want to see if the parent node of the current node I am referring to is equal to a particular value. I did it as follows but no use.
eg.
<v:name>
<v:age>
when at "age" I tried <xsl:if test='.. = v:name'>. But it is not correct. What is the correct way? Can someone help?
I am inside a template which is true for both v:name and v:age. There are v:age's which are not children of v:name. I want to ensure that v:age I am referring to is a child of a v:name. That is what I want inside the test attribute.
From a comment by the OP:
I am inside a template which is true for both v:name and v:age.
There are v:age's which are not children of v:name. I want to
ensure that v:age I am referring to is a child of a v:name. That
is what I want inside the test attribute
Use:
parent::v:name
And this in a xsl:if becomes:
<xsl:if test="parent::v:name">
<!-- Whatever processing is necessary. -->
</xsl:if>
Use <xsl:if test="../v:name = 'somevalue'></xsl:if> or <xsl:if test="parent::v:name = 'somevalue'></xsl:if>
I suspect that when you say you want to know if the parent node is "equal" to a particular value, you really mean that you want to know whether its name is equal to a particular value (this would be immediately clear if you gave examples of your input and output).
If my conjecture is correct, use <xsl:if test="parent::xyz">

msxsl:node-set() and position() are not working together

I've got an xsl:variable that contains a pre-processed set (DoesNotContainChildElement). I think msxsl:node-set() is adding a root element so position is always 1. But what I need it the top 15 elements.
<xsl:variable name="Summary">
<xsl:for-each select="msxsl:node-set($DoesNotContainChildElement)">
<xsl:if test="position() < 16">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:if>
</xsl:for-each>
No, the function msxsl:node-set does not add any root node, it is simply that with XSLT 1.0 a sample like
<xsl:variable name="rtf1">
<item>1</item>
<item>2</item>
<item>3</item>
</xsl:variable>
creates a result tree fragment and "A result tree fragment is treated equivalently to a node-set that contains just a single root node". So in the sample above we have a result tree fragment with a single root node containing three item child elements.
Applying the msxsl:node-set(rtf1) extension function then gives you a node-set instead of a result tree fragment where now the node-set contains a single root node with three item child elements. Thus if you want to access the item elements you need msxsl:node-set($rtf1)/* or more general msxsl:node-set($rtf1)/node() to access all child nodes.
You may try msxsl:node-set($DoesNotContainChildElement)/*. If it's true that msxsl:node-set() adds a root node, then adding /* to your path will iterate over the children, where you can test for position.
Alternatively you could simply use <xsl:apply-templates select="msxsl:node-set($DoesNotContainChildElement)/*" mode="testposition"/> and <xsl:template match="*" mode="testposition">….
There's only one variable $DoesNotContainChildElement so a for-each will only yield value 1 for position().
You can check that by running the following:
<xsl:for-each select="$x">
<pos1><xsl:value-of select="position()"/></pos1>
</xsl:for-each>
<xsl:for-each select="$x/*">
<pos2><xsl:value-of select="position()"/></pos2>
</xsl:for-each>
Where x is a (node-set type) variable.
The result will look something like
<pos1>1</pos1>
<pos2>1</pos2>
<pos2>2</pos2>
<pos2>3</pos2>
<pos2>...</pos2>
Adding <xsl:copy-of select="."/> will result in the output of the entire variable contents in case of the first for-each above, whereas for the second for-each it will result in the output of each sub-element of the variable one-by-one.
The second form is the one to use if you wish to output only selected sub-elements.
The same holds when you first apply the node-set function to change an rtf into a node-set.