[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] node() function

Subject: Re: [xsl] node() function
From: "Abel Braaksma (Exselt) abel@xxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 24 Jun 2014 00:01:47 -0000

Hi Dak,

Please see my answers inline,


Abel Braaksma
Exselt XSLT 3.0 streaming processor

Mailing Lists Mail daktapaal@xxxxxxxxx <mailto:xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
maandag 23 juni 2014 22:17

<xsl:template match="node()|@*">

The expression "node()" is not a function, it is a node-test, similar to "foo" (tests for an element with the name 'foo') or "comment()" (tests for comment nodes). The template is matched when _any_ node is found. Since inside the body of the template you only have an xsl:apply-templates and the only template in existence is that very template, the stylesheet as a whole will not output anything: a so-called "delete-template".

Remember that when you do apply-templates without argument, you select all children (but not attributes) and ask the processor to find matching templates. In this case, this is the template with match="node()", which means "match anything, including text, comment and PI nodes, except attributes". The part of the expression that matches attributes has not effect, because you do not apply templates on attributes.

<xsl:template match="*"> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet>

This looks similar, but is not. The match="*" only matches elements. That means, that if the xsl:apply-templates encounters text nodes, there is no matching template, so the default template will kick in. That is the reason why this one does, and the previous one does not give you text output.

<xsl:template match="/"> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet>

This looks similar, but is totally different. With one input document there will usually be only one root node that can ever be matched. The expression "/" matches that root node. Your template will be hit once, the apply-templates selects its children, which do not have a matching template, so the default templates kick in: text is output, similar, but not the same, as with the previous code, your #2.

I was expecting all the three stylesheets to copy the text nodes to the target.

No, only the last two, the first was different, it was written to delete all content of the input document.

While the stylesheet2 and stylesheet 3 did that, the stylesheet 1 did not output anything ( wondered why?? )..

What I was thinking the stylesheet1 will do is :

1. Match any node() or the attribute node .

That is correct, that is what it did.

2. Apply template to the children and self

No, you only applied templates to the children if each current node recursively, not to self (which would have led to an eternal loop if you did that).

3. Default template rule will kick in as I havenbt mentioned any node. This will :

Yes you "mentioned" a node: any node, to be precise: every node is matched if you use node().

a. Do value-of select for text nodes

Yes, but you specified differently, so the default template never kicked in.

b. Do apply-templates for the element nodes ( * )

No, it would do apply-templates for all nodes, except attribute nodes.
SO the answer was in the fact that node() does not match text()??

No, the answer was that "node()" _also_ matches text-nodes. It matches every node. But you did not do a value-of yourself inside your template, so nothing was output.

So I added <xsl:template match="text()"> <xsl:value-of select="."/> </xsl:template>

Yes, that works. Now you specified _explicitly_ to make an exception for text-nodes, which takes precedence over your match="node()", so the result is that the text is matched in the added template, and your value-of makes sure that the text is output.

This came with what I wanted.. ( both happy and disappointed )

Happy as it brought me to a logical end, and disappointed as it dint
work like I initially thought it would.

Hope that the above explanations helped a bit.

Further, This leads me to a (dangerous) way of saying : Select only node c and nothing else.

The XSLT way of doing that is: write a delete-template (your example #1 above) and add an exception for the c-node, full stylesheet body could look like this:

<xsl:template match="node()|@*">

<xsl:template match="c">
<!-- copy all children, do not process children further -->
<xsl:copy-of select="." />

Your original example serves as a "delete everything", so all you need to do is specify what you do not want to delete, and copy that. Instead of xsl:copy-of you can also do xsl:value-of or do some further processing on the children. That is the beauty of XSLT: you specify with templates what you want output, the processor does the necessary processing logic behind the scenes.

I could do :
1. The normal intuitive way ( Approach A)
<xsl:template match="/">
<xsl:apply-templates select = bcb/>
<xsl:template match="c">
<xsl:value-of select="."/>

Not sure this is the "normal intuitive way" in XSLT. It only works if you have a root node called "c", no other nodes will ever be processed (you match "/", and then you tell the processor to select "c" children, which can at most be one, because the root node can have only one child).

2. The somewhat dangerous way (based on the observation in PART1
) ( Approach B )

<xsl:template match="node()|@*">
<xsl:template match="c">
<xsl:value-of select="."/>

No, this is the _safe_ way, not the dangerous way. Your observation led you to the wrong conclusions, but to the correct solution. This is precisely how you would specify it in XSLT.

<!bOther nodes will not be cared for or other nodes does nothing (but why??)

I would have thought, the other nodes will be matched, and text nodes
be printed, but did not, as in PART1. (= reasons for calling Part 2
as corollary to part 1)

Yes, other nodes (all nodes) are matched, every time, inside the match="node()|@*" template. But there is no xsl:value-of or xsl:copy or anything that creates output, so the result is empty, except, in your last example, for the (text value of the) c-element.

Approach B is not intuitive for me. But somehow doing the same thing as approach A. Although I will NEVER use the approach B.

Welcome to the world of XSLT: instead of writing step by step what you want the processor to do (your approach A, which is not flexible and will hardly ever work), you can write templates, and the processor will simply process your templates when it finds nodes that match them. In fact, you tell in your stylesheet how you want the output to look like based on certain input (which you specify in the match-attribute) and the processor does the heavy lifting for you. Result: no loops required, no recursion required to process children and children of children, nothing of the kind.

May I suggest taking a little moment of reading a few chapters of an XSLT beginners book? The ones by Jeni Tennison are still excellent material. It explains this way of thinking and once you get the hang of it you wonder why you ever wanted to do it differently ;).

Any Idea why this is so.. are there situations where approach B wont
work? I want to think approach B is Wrong and will fail some how..
Anything can go wrong, depending on how you program, what your requirements are. XSLT is a powerful language and as with any programming language, a programmer will, at one point or another, introduce bugs in a program, usually when the program code starts to grow beyond the mere trivial.

But if the requirement is "output the text value of node X" then approach B is excellent. In fact, in most cases, you either choose an approach B of the form "delete everything, and keep this and that", or "copy everything, but delete this and that" (which is commonly referred to as the "XSLT copy idiom", but that is excellently covered in the XSLT Cookbook, 1st edition for XSLT 1.0 and 2nd edition for XSLT 2.0, both are available online free of charge).


Current Thread