Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT apply-template question

Tags:

xml

xslt

I am confused about XSLT apply-template statement. For example, here in w3school.

http://www.w3schools.com/xsl/xsl_apply_templates.asp

For the statements,

<xsl:template match="/">
  <html>
  <body>
  <h2>My CD Collection</h2>
  <xsl:apply-templates/>
  </body>
  </html>
</xsl:template>

my confusions are,

(1) what is the function of <xsl:apply-templates/>? It does not contain any specific template to call. I think it will match (return) all directly child of current element (non-direct child of current node will not be returned, current node is root node), not sure whether I am correct?

(2) after all matched nodes are returned in (1), what are the next step XSLT processor will do?

(3) in this specifc sample, the root node is catalog or another higher level of root? and why?

thanks in advance, George

like image 601
George2 Avatar asked Dec 29 '09 09:12

George2


3 Answers

Some things that will make understanding the answers you get easier:

First and foremost, nodes and elements are not the same thing. Elements are nodes, but nodes aren't necessarily elements. You often find people using the terms interchangeably. There are actually four kinds of nodes in XML: elements, text nodes, processing instructions, and comments. (Attributes aren't really nodes, which I'll get to in a second.)

In XSLT, the root of an XML document isn't its top-level element; the root is an abstraction that doesn't really exist. The top-level element is a child of the root. For instance, here's a well-formed XML document whose root has five child nodes, including the top-level element:

<?xml-stylesheet href="mystyle.css" type="text/css"?>
<!-- this is a perfectly legitimate XML document -->
<top_level_element/>

Five? It looks like there are only three. I think I'll let you figure out what the other two are yourself. Hint: there could actually be seven nodes in that example.

The XPath expression / finds the document root, not the top-level element. In the above case, to find the top-level element, you'd use /top_level_element, or /*. (It's always safe to use /* to find the top-level element, since the document root must have a single element child.)

So armed with that knowledge, let's look at what apply-templates does. It basically performs a two-step process: First, it builds a set of nodes. Then, and for each one, it finds a matching template (from among the templates in the XSLT file) and applies the template to it. As you observed in one of your comments, it's conceptually very similar to a loop.

The select attribute is used in the first step. It provides an XPath expression that's used to build the node set that it's going to apply templates to. If no select attribute is provided, the list it builds is all children of the context node. (The "context node" is the node that the current template is being applied to.)

The match attribute on the template elements is used in the second step. The stylesheet processor finds all templates whose match attribute matches the node it's trying to apply templates to. If it finds more than one, it selects the most specific one it can, e.g. given these templates:

<xsl:template match="*"/>
<xsl:template match="foo"/>
<xsl:template match="foo[bar]"/>

a foo element with a bar child element will be matched by the third, a foo element with no bar will be matched by the second, and a baz element will be matched by the first. (The actual method that XSLT uses is defined here; in practice, I've been using XSLT for nearly a decade and I've never once needed to know specifically how it works, though it's interesting.)

If it doesn't find a match, it will use the built-in default template for the type of node - basically, you can assume that any XSLT transform implicitly contains these templates:

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

<xsl:template match="text()">
    <xsl:copy/>
</xsl:template>

<xsl:template match="processing-instruction() | comment() | @*"/>

Armed with all this knowledge, you can now understand the identity transform:

<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
</xsl:template>

That matches any node or attribute (note that attributes aren't nodes, which is why @* is needed), copies it, and then applies templates to all of its child nodes and attributes. (Only the document root and elements will have child nodes, and only elements will have attributes.) Since it's the only template in the transform, and it matches all nodes and attributes, it applies itself to all of the child nodes and attributes. Thus, it copies everything in the source tree to the output tree.

If you add this template to the identify transform:

<xsl:template match="foo"/>

you now have a transform that copies every node in the source tree except foo elements - that second template matches foo elements (the first one does too, but since the second one's match attribute is more specific, it's the one XSLT chooses) and does nothing with them.

Given all that, the answers to your specific questions:

  1. <xsl:apply-templates> applies templates to the children of the context node.

  2. Matched nodes aren't "returned" in step 1; the XSLT processor finds a template for each and applies it.

  3. In this example, the context node is the document root, an abstract node that the top-level element and any comments or processing instructions outside it are the children of.

like image 128
Robert Rossney Avatar answered Oct 27 '22 11:10

Robert Rossney


  1. <xsl:apply-templates /> will try to find a template which matches current node and its children.
  2. After all matched nodes are returned, XSL processor will output those closing tags (i.e. </body> and </html>)
  3. There is a root node, just before catalog, matched by "/"

EDIT: An example to clarify 1.; consider your provided sample:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/">
      Here we're at "root" node, just before "catalog" element.<br />
      Let's enumerate this child nodes:
      <ul>
      <xsl:for-each select="*">
          <li><xsl:value-of select="name()" /></li>
      </xsl:for-each>
      </ul>
      <!-- Now, process "catalog" and ALL his child nodes -->
      <xsl:apply-templates/>  
    </xsl:template>

    <xsl:template match="cd">
      <p>
        <!-- Find a template ONLY for title element -->
        <xsl:apply-templates select="title"/> 
        <xsl:apply-templates select="artist"/>
      </p>
    </xsl:template>
</xsl:stylesheet>
like image 36
Rubens Farias Avatar answered Oct 27 '22 10:10

Rubens Farias


xsl:apply-templates directs the XSLT engine to match the current source document subnodes against the stylesheet templates for further processing.

like image 34
Ignacio Vazquez-Abrams Avatar answered Oct 27 '22 11:10

Ignacio Vazquez-Abrams