[ACCEPTED]-In what order do templates in an XSLT document execute, and do they match on the source XML or the buffered output?-xslt
I love your question. You're very articulate 134 about what you do not yet understand. You 133 just need something to tie things together. My 132 recommendation is that you read "How XSLT Works", a chapter 131 I wrote to address exactly the questions 130 you're asking. I'd love to hear if it ties 129 things together for you.
Less formally, I'll 128 take a stab at answering each of your questions.
- In what order do the templates execute, and
- When they execute, do they match on (a) the original source XML, or (b) the current output of the XSLT to that point?
At 127 any given point in XSLT processing, there 126 are, in a sense, two contexts, which you 125 identify as (a) and (b): where you are in 124 the source tree, and where you are in the result tree. Where you 123 are in the source tree is called the current node. It 122 can change and jump all around the source 121 tree, as you choose arbitrary sets of nodes 120 to process using XPath. However, conceptually, you 119 never "jump around" the result 118 tree in the same way. The XSLT processor 117 constructs it in an orderly fashion; first 116 it creates the root node of the result tree; then 115 it adds children, building the result in 114 document order (depth-first). [Your post 113 motivates me to pick up my software visualization 112 for XSLT experiments again...]
The order 111 of template rules in a stylesheet never 110 matters. You can't tell, just by looking 109 at the stylesheet, in what order the template 108 rules will be instantiated, how many times 107 a rule will be instantiated, or even whether 106 it will be at all. (
match="/" is an exception; you 105 can always know that it will get triggered.)
I 104 am assuming that Template #1 will execute 103 first. I don't know why I assume this 102 -- is it just because it appears first 101 in the document?
Nope. It would be called 100 first even if you put it last in the document. Template 99 rule order never matters (except under an 98 error condition when you have more than 97 one template rule with the same priority 96 matching the same node; even then, it's 95 optional for the implementor and you should 94 never rely on such behavior). It gets called 93 first because the first thing that always happens 92 whenever you run an XSLT processor is a 91 virtual call to
<xsl:apply-templates select="/"/> . The one virtual call constructs 90 the entire result tree. Nothing happens 89 outside it. You get to customize, or "configure", the 88 behavior of that instruction by defining 87 template rules.
Will Template #2 execute? It 86 matches a node in the source XML, but by 85 the time the we get to this template (assuming 84 it runs second), the "firstName" node 83 will not be in the output tree.
Template 82 #2 (nor any other template rules) will never 81 get triggered unless you have an
<xsl:apply-templates/> call somewhere 80 in the
match="/" rule. If you don't have any, then 79 no template rules other than
match="/" will get triggered. Think 78 of it this way: for a template rule to get 77 triggered, it can't just match a node in 76 the input. It has to match a node that you 75 elect to process (using
<xsl:apply-templates/>). Conversely, it will 74 continue to match the node as many times 73 as you choose to process it.
match="/"template] pre-empt 72 all other templates from executing since 71 there is nothing to match on after that 70 first template is complete?
That rule preempts 69 the rest by nowhere including
<xsl:apply-templates/> in it. There 68 are still plenty of nodes that could be processed 67 in the source tree. They're always all there, ripe 66 for the picking; process each one as many 65 times as you want. But the only way to process 64 them using template rules is to call
To 63 this point, I've been concerned with later 62 templates not executing because the nodes 61 they have operated on do not appear in 60 the output, but what about the inverse? Can 59 an "earlier" template create 58 a node that a "later" template 57 can do something with?
It's not that an 56 "earlier" template creates a new 55 node to be processed; it's that an "earlier" template 54 in turn processes more nodes from the source 53 tree, using that same instruction (
<xsl:apply-templates). You 52 can think of it as calling the same "function" recursively, with 51 different parameters each time (the nodes 50 to process as determined by the context 49 and the
In the end, what you 48 get is a tree-structured stack of recursive 47 calls to the same "function" (
<xsl:apply-templates>). And 46 this tree structure is isomorphic to your actual result. Not 45 everyone realizes this or has thought about 44 it this way; that's because we don't have 43 any effective visualization tools...yet.
Template 42 #1 creates a new node called "fullName". Template 41 #2 matches on that same node. Will Template 40 #2 execute because the "fullName" node exists 39 in the output by the time we get around 38 to Template #2?
Nope. The only way to do 37 a chain of processing is to explicitly set 36 it up that way. Create a variable, e.g.,
$tempTree, that 35 contains the new
<fullName> element and then process 34 it, like this
<xsl:apply-templates select="$tempTree">. To do this in XSLT 1.0, you 33 need to wrap the variable reference with 32 an extension function (e.g.,
exsl:node-set()), but in XSLT 31 2.0 it will work just as is.
Whether you're 30 processing nodes from the original source 29 tree or in a temporary tree that you construct, either 28 way you need to explicitly say what nodes 27 you want to process.
What we haven't covered 26 is how XSLT gets all its implicit behavior. You 25 must also understand the built-in template rules. I write stylesheets 24 all the time that don't even include an 23 explicit rule for the root node (
match="/"). Instead, I 22 rely on the built-in rule for root nodes 21 (apply templates to children), which is 20 the same as the built-in rule for element 19 nodes. Thus I can ignore large parts of 18 the input, let the XSLT processor automatically 17 traverse it, and only when it comes across 16 a node I'm interested in will I do something 15 special. Or I could write a single rule 14 that copies everything recursively (called 13 the identity transform), overriding it only 12 where necessary, to make incremental changes 11 to the input. After you've read "How 10 XSLT Works", your next assignment is 9 to look up the "identity transform".
I 8 realize that I'm deeply ignorant about 7 the "zen" of XSLT. To date, my stylesheets 6 have consisted of a template matching 5 the root node, then are completely procedural 4 from there. I'm tired of doing this. I 3 would rather actually understand XSLT correctly, hence 2 my question.
I applaud you. Now it's time 1 to take the "red pill": read "How XSLT Works"
Templates always match in the source XML. So the 4 order doesn't really matter, unless 2 or 3 more templates match the same node(s). In 2 that case, somewhat counter-intuitively, the 1 rule with the last matching template is triggered.
In your 1st example Template #1 runs because 30 when you start processing the input xml 29 it begins at the root and that is the only 28 template in your stylesheet that matches 27 the root element. Even if it was 2nd in 26 the stylesheet it would still run 1st.
In 25 this example template 2 will not run as 24 you have already processed the root element 23 using template 1 and there are no more elements 22 to process after the root. If you did want 21 to process other elements using additional 20 templates you should change it to.
<xsl:template match="/"> <xsl:apply-templates/> </xsl:template>
This then 19 allows you to define a template for each 18 element you are interested in and process 17 the xml in a more logical way, rather than 16 doing it procedurally.
Also note that this 15 example will not output anything as at the 14 current context (the root) there is no firstName 13 element, only a person element so it should 12 be:
<xsl:template match="/"> <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/> </xsl:template>
I find it easier to think that you are 11 stepping through the xml, starting at the 10 root and looking for the template that matches 9 that element then following those instructions 8 to generate teh output. The XSLT transforms 7 the input document to the output so the 6 output doucument is empty at the start of 5 the transformation. The output is not used 4 as part of the transformation it is just 3 the output from it.
In your 2nd example Template 2 #2 will not execute because the template 1 is run against the input xml not the output.
Evan's answer is basically a good one.
However 23 one thing which does seem to be lacking 22 is the ability to "call" up chunks of code 21 without doing any matching. This would - at 20 least in some people's opinion - enable 19 much better structuring.
I have made a small 18 example in an attempt to show what I mean.
<xsl:template match="/" name="dotable"> <!-- Surely the common html part could be placed somewhere else --> <!-- the head and the opening body --> <html> <head><title>Salary table details</title></head> <body> <!-- Comments are better than nothing --> <!-- but that part should really have been somewhere else ... --> <!-- Now do what we really want here ... this really is making the table! --> <h1>Salary Table</h1> <table border = "3" width="80%"> <xsl:for-each select="//entry"> <tr> <td><xsl:value-of select="name" /></td> <td><xsl:value-of select="firstname" /></td> <td><xsl:value-of select="age" /></td> <td><xsl:value-of select="salary" /></td> </tr> </xsl:for-each> </table> <!-- Now close out the html --> </body> </html> <!-- this should also really be somewhere else --> <!-- This approach works, but leads to horribly monolithic code --> <!-- Further - it leads to templates including code which is strictly --> <!-- not relevant to them. I've not found a way round this yet --> </xsl:template>
However, after 17 fiddling around a bit, and at first making 16 use of the hint that if there are two matching 15 templates the last one in the code will 14 be selected, and then restructuring my code 13 (not all shown here), I achieved this which 12 seems to work, and hopefully generates the 11 correct code, as well as displaying the 10 wanted data -
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- <?xml version="1.0"?>--> <xsl:template name="dohtml"> <html> <xsl:call-template name="dohead" /> <xsl:call-template name="dobody" /> </html> </xsl:template> <xsl:template name="dohead"> <head> <title>Salary details</title> </head> </xsl:template> <xsl:template name="dobody"> <body> <xsl:call-template name="dotable" /> </body> </xsl:template> <xsl:template match="/entries" name="dotable"> <h1>Salary Table</h1> <table border = "3" width="80%"> <xsl:for-each select="//entry"> <tr> <td><xsl:value-of select="name" /></td> <td><xsl:value-of select="firstname" /></td> <td><xsl:value-of select="age" /></td> <td><xsl:value-of select="salary" /></td> </tr> </xsl:for-each> </table> </xsl:template> <xsl:template match="/" name="main"> <xsl:call-template name="dohtml" /> </xsl:template>
[Scroll the code above up-down if you can't see it all]
The way this works is the 9 main template always matches - matches on 8 /
This has the chunks of code - templates 7 - which are called.
This now means that it 6 is not possible to match another template 5 on / but it is possible to match explicitly 4 on a named node, which in this case is the 3 highest level node in the xml - called entries.
A 2 small modification to the code produced 1 the example given above.
More Related questions