Content is king and sometimes the king does not want to be told what to do. What do we do? Revolt.
Copy-of instruction’s Achilles heel
The problem with <xsl:copy> and <xsl:copy-of> is that it’s useful only if you don’t need to modify its content. Let’s take a look at a sample XML that will be used throughout this article:
<body>
<h3 id="tips">Ninja 101</h3>
<p>Ninjas are <em>not</em> about killing, it's about devotion.</p>
<p>You will do well to heed to the following. Learn to:</p>
<ul class="skills">
<li>Conceal</li>
<li>Strafe</li>
<li><a href="#tango">Tango</a></li>
</ul>
<p>Only a true ninja can summon the courage to sing karaoke in public.</p>
</body>
Using <xsl:copy-of select="body/*"/> will display the above pixel-to-pixel to your output sans the <body> node but that leaves you with no room to modify the source. We need a fresh approach to this problem.
Something more applicable
Here are a few things that we know we must do:
- XSLT is naturally good at matching nodes and HTML elements are just that; nodes.
- Elements may include either text and/or other elements.
- Elements may have one or more attribute(s).
With that in mind, let’s set out to produce code alpha.
Firstly we need to change from using <xsl:copy-of/> to the new and improved way: <xsl:apply-templates select="body" />. This will begin the apply process starting from <body>. The corresponding matching code for it would be:
<xsl:template match="body/*">
<xsl:element name="{name()}"/>
</xsl:template>
This rule says, match all children elements of <body>. The element instruction is used to reproduce the element in context. This is what the output looks like:
<h3/>
<p/>
<p/>
<ul/>
<p/>
Well, it’s a good start. So far we’ve managed to output all the top level elements. Next step is to grab the text elements in the each of the nodes in code beta:
<xsl:template match="body/*">
<xsl:element name="{name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
Alternatively one can use <xsl:value-of select="."/>. In the above code, the use of <xsl:apply-templates/> also works because in the event that there are no matching nodes found, the built-in template rule will be applied. Built-in template rules simply copy the node and outputs it as plain text. Conveniently, the non-matched nodes in our case happens to be text nodes.
More importantly, <xsl:apply-templates/> serves a much greater purpose and this will be evident soon. But before that, let’s take a look at the result:
<h3>Ninja 101</h3>
<p>Ninjas are not about killing, it's about devotion.</p>
<p>You will do well to heed to the following. Learn to:</p>
<ul>
Conceal
Strafe
Tango
</ul>
<p>Only a true ninja can summon the courage to sing karaoke in public.</p>
It’s now looking pretty good. Next up, we will tackle elements embedded inside another element. We will need to figure out a way so the template will be perpetually applied. Luckily, it was good foresight that we used <xsl:apply-templates/> didn’t we? Let’s take a look at code gamma.
<xsl:template match="body//*">
<xsl:element name="{name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
The changed line here is body//*. The <xsl:apply-templates/> instruction is already doing the hard work for us, all we needed to do was to change the template’s Pattern1 to encompass all children nodes of <body>. The sizzling result is as shown:
<h3>Ninja 101</h3>
<p>Ninjas are <em>not</em> about killing, it's about devotion.</p>
<p>You will do well to heed to the following. Learn to:</p>
<ul>
<li>Conceal</li>
<li>Strafe</li>
<li><a>Tango</a></li>
</ul>
<p>Only a true ninja can summon the courage to sing karaoke in public.</p>
As the ninjas would say, “Splendid, sir. Would you like more wine?” If you’re not excited by now then you must be a robot. A cold, evil robot – and robots can’t be ninjas.
The only things missing are attributes. Here’s code delta:
<xsl:template match="body//*">
<xsl:element name="{name()}">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="body//@*">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
We’ve added a new line in the first template: <apply-templates select="@*"/>. The same logic for multiple nested elements also applies to attributes so it’s important to use apply-templates in this case to match all the attribute nodes.
Here’s the final result:
<body>
<h3 id="tips">Ninja 101</h3>
<p>Ninjas are <em>not</em> about killing, it's about devotion.</p>
<p>You will do well to heed to the following. Learn to:</p>
<ul class="skills">
<li>Conceal</li>
<li>Strafe</li>
<li><a href="#tango">Tango</a></li>
</ul>
<p>Only a true ninja can summon the courage to sing karaoke in public.</p>
</body>
More elegant XSLT
The code now works great but the apply template rule can be abbreviated:
<xsl:template match="body//*">
<xsl:element name="{name()}">
<xsl:apply-templates select="* | @* | text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="body//@*">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
Through the power of the union (|) operator, we can combine the instructions into a single one.
So…great, we’ve managed to achieve exactly what <xsl:copy-of/> could’ve done in one line. Yawnfest I hear people say. Fear not, the good bit is coming.
Exercise for the body and mind
Brain teaser time. Let’s give ourselves some ninja-worthy exercises (with increasing difficulty for extra ninjaness):
- Change
<h3>to<h4>. - Output only elements before the second
<p>element. - Only display one
<li>element.
The first one sounds easy enough, the second and third could be a little trickier.
Exercise 1: Modifying the heading
The beauty of template matching is that you can always override it. templates have an attribute called “priority” which you can set so a template matching the same element can take precedence over another. It’s kind of like using the !important rule in CSS.
<xsl:template match="h3" priority="1">
<xsl:element name="h4">
<xsl:apply-templates select="* | @* | text()"/>
</xsl:element>
</xsl:template>
As you can see this is conceptually the same as the rule we used to match all elements inside <body>, except the match is more specific and a priority value of 1 is given (the default is 0). And that’s it! This additional template was the only thing needed to change the source <h3> into <h4>!
I won’t spoil the fun for you all so I’m going to allow you guys to mull over rest of the exercises. Truth be told, I haven’t done them myself so any of you who figured it out before I do, please share it with us! If no one manages to solve them, then I’ll post up a solution in a future article.
Now that you know how to display your body content the ninja way, I encourage everyone who’s read this article to assassinate <xsl:copy-of/> and embrace the shuriken that is <apply-templates/>.
Discussion
Have you managed to solve ninja exercises one and two? Are you a Japanese-Indian Ninja Guru? Why not share your findings, eureka moments, despair or frustration with us?
Footnotes
- It’s important to note that Patterns are different to XPath. Not all XPath syntax is allowed in a Pattern. ↩



SpideyMizzou 14 June 07