tags:

views:

206

answers:

3

I think I know the answer to this, but I just want confirmation I'm understanding this correctly.

When an XSLT template matches and executes, the children of the current node (the node current having a matching template executed) are not processed by default. You must call "apply-templates" to get the processor to traverse down into the child nodes of the current (matched) node.

Consider this XML:

<person>
  <firstName>Deane</firstName>
  <lastName>Barker</lastName>
</person>

During the transform, the XSLT processor starts at the "person" element. If it finds a template for that, it will execute it, but then will not descend into the "firstName" and "lastName" elements to look for templates for those. It will only do this if "apply-templates" is explicitly called in the template for "person".

Consider this XSL:

<!-- Template #1 -->
<xsl:template match="person">
  I found a "person" element
</xsl:template>

<!-- Template #2 -->
<xsl:template match="firstName">
  I found a "firstName" element
</xsl:template>

In this case, Template #2 will not be run, correct? The traversal of the XML document will hit the "person" element, find Template #1, execute it, then never descend into the children of "person."

If I change the first template to this --

<!-- Template #1 -->
<xsl:template match="person">
  I found a "person" element
  <xsl:apply-templates select="firstName"/>
</xsl:template>

Only then will Template #2 run. Am I understanding this correctly?

+1  A: 

Yes, that is correct. You do have to explicitly call apply-templates to traverse further down the tree. You can always use the more generic <xsl:apply-templates /> (without a @select) to apply templates to every child node (excepting that element's attributes, which must be explicitly selected using select="@*").

However, you can also use <xsl:copy-of select="person" />, which will make a direct copy of any selected elements and all of their children, and so on.

James Sulak
Nitpicking: `<xsl:apply-templates />` (without a @select) applies templates to every child *node*, not element (except attributes, which are not treated as children).
Tomalak
Due to this bug (http://meta.stackoverflow.com/questions/18360/vote-too-old-to-be-changed-but-i-havent-voted), I can't up-vote your answer. Sorry.
Tomalak
Thanks, I misspoke.
James Sulak
Yay. Now that you've edited, I can up-vote. The reason for this silly artificial limitation in SO's voting system is beyond me.
Tomalak
A: 

Yes, you are right. As you may already know, it also depends on how you "enter" into the apply-templates logic. In the above example, If you did not have the template for "person", then the default template rules would have been fired (for all child nodes from the point where apply-templates was called).

But if any of the templates(that match any childnodes in an apply-templates call) have been defined in your stylesheet, applying templates to childnodes from that node onwards would be dictated by the template calling apply-templates again, otherwise they will just be ignored.

Thiyagaraj
+2  A: 

Agreed to both answers so far; your assessment that Template #2 won't get processed as such is exactly right.

Note that a node's children (i.e. its child elements, comments, text, and processing instructions, but not attributes) are only special in two ways:

  1. The default value of the select attribute is "node()", the XPath for selecting children, and
  2. The built-in template rules for root nodes and element nodes is to process children.

Other than in those two respects, children have no special status when it comes to selecting nodes to process. You can select whatever nodes you want, including ancestors. You can traverse a tree in any order you like. An extreme way to demonstrate this would be to write a stylesheet that reverses the hierarchy of a document (turns it upside down). (You'd probably want to use another mode so that a different rule is invoked when parent nodes are processed than when children are processed.)

I also want to check and make sure you understand the distinction between the "root node" (renamed the "document node" in XPath/XSLT 2.0) and the root (or document) element. In your example XML, <person> is not the root node; it's the outermost element, which itself is a child of the root node, the invisible outer container at the root of every tree in the XPath/XSLT data model.

If you want to take control of processing right of the bat, you can always use <xsl:template match="/"> or (assuming the source tree represents a well-formed XML document and not a fragment) <xsl:template match="/*">. They're alternative ways to do it. One feature of the latter is that imported code that includes a rule for match="/" would be triggered earlier, since explicit template rules (even if they have the lowest import precedence) always take precedence over built-in template rules. If you don't explicitly have <xsl:template match="/"> in the importing stylesheet, then, until you import code that has it, you're relying on the built-in template rule for root nodes (apply templates to children--in this case, apply templates to the <person> element child).

An alternative way to take control off the bat, for this example, would be to use match="/person". Such a rule would match any <person> element, provided that it's the child of the root node. If the XML doesn't have <person> as its outermost element, then it won't get invoked. And if you also have <xsl:template match="/", then it would get invoked first; the match="/person" (or match="/*") would only get invoked if the match="/" rule explicitly applied templates to children.

Evan Lenz