XSLT, finding out if last child node is a specific element
Solution 1
Just select the last node of the <foo>
element and then use self
axis to resolve the node type.
/foo/node()[position()=last()]/self::bar
This XPath expression returns an empty set (which equates to boolean false) if the last node is not an element. If you want to specifically get value true
or false
, wrap this expression in the XPath function boolean()
. Use self::*
instead of self::bar
to match any element as the last node.
Input XML document:
<root>
<foo>some text <bar/> and maybe some more</foo>
<foo>some text <bar/> and a last <bar/></foo>
</root>
XSLT document example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="foo">
<xsl:choose>
<xsl:when test="node()[position()=last()]/self::bar">
<xsl:text>bar element at the end </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>text at the end </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output of the stylesheet:
text at the end
bar element at the end
Solution 2
Now I am in
foo
, and want to find out if the last child is abar
Use:
node()[last()][self::bar]
The boolean value of any non-empty node-set is true()
and it is false()
for otherwise. You can use the above expression directly (unmodified) as the value of the test
attribute of any <xsl:if>
or <xsl:when>
.
Better, use:
foo/node()[last()][self::bar]
as the match
attribute of an <xsl:template>
-- thus you write in pure "push" style.
Solution 3
Update: This answer addresses the requirement stated in the original question title, "finding out if last child node is a text node." But the question body suggests a different requirement, and it seems that the latter requirement was the one intended by the OP.
The previous two answers explicitly test whether the last child is a bar
element, rather than directly testing whether it is a text node. This is correct if foo contains only "mixed text nodes and bar elements" and never has zero children.
But you may want to test directly whether the last child is a text node:
- For readability of stylesheet logic
- In case the element contains other children besides elements and text: e.g. comments or processing instructions
- In case the element has no children
Maybe you know the latter two will never occur in your case (but from your question I would guess that #3 could). Or maybe you think so but aren't sure, or maybe you hadn't thought about it. In either case, it's safer to test directly for what you actually want to know:
test="node()[last()]/self::text()"
Thus, building on @Dimitre's example code and input, the following XML input:
<root>
<foo>some text <bar/> and maybe some more</foo>
<foo>some text <bar/> and a pi: <?foopi param=yes?></foo>
<foo>some text <bar/> and a comment: <!-- baz --></foo>
<foo>some text and an element: <bar /></foo>
<foo noChildren="true" />
</root>
With this XSLT template:
<xsl:template match="foo">
<xsl:choose>
<xsl:when test="node()[last()]/self::text()">
<xsl:text>text at the end; </xsl:text>
</xsl:when>
<xsl:when test="node()[last()]/self::*">
<xsl:text>element at the end; </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>neither text nor element child at the end; </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
yields:
text at the end;
neither text nor element child at the end;
neither text nor element child at the end;
element at the end;
neither text nor element child at the end;
Comments
-
Johan almost 2 years
Look at the following two examples:
<foo>some text <bar/> and maybe some more</foo>
and
<foo>some text <bar/> and a last <bar/></foo>
Mixed text nodes and
bar
elements within thefoo
element. Now I am infoo
, and want to find out if the last child is abar
. The first example should prove false, as there are text after thebar
, but the second example should be true.How can I accomplish this with XSLT?
-
Max Toro over 13 yearsYou can also simplify the predicate
/foo/node()[last()]/self::bar
-
Admin over 13 yearsjasso: +1 Good answer. I also think that
[last()]
predicate is better -
Admin over 13 yearsGood addition. But preceding answers didn't change OP requeriments as it seems suggested by your first sentence. From question:
want to find out if the last child is a bar
. -
LarsH over 13 years@Alejandro: good point. I didn't notice that the OP himself changed the requirements, between the title and the sentence you quoted. I was going by the title.