Content is king and sometimes the king does not want to be told what to do. What do we do? Revolt.
It has been known in the Symphony universe that when you want to output your content in XSLT, you do so with the <xsl:copy-of> instruction. For the most part, it serves the public well but for those who crave power, fame and fortune should do well to learn the following ninja technique; “Don’t comply. Apply”.
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