Wednesday, October 11, 2006

XSLT: Use of the self:: axis

I'm Peter Cooper, one of the software engineers working with Checkmate Technologies, and a lot of my job right now involves using XML and XSLT. One of the things that's kind of confusing when you first look at XSLT is all the axes that you can use, and why you'd want to use them. Things like child::, descendent::, and parent:: make a lot of sense, but what about self::? Doesn't it seem kind of pointless to have an axis that just gives you nodes where you already are?

However, after working with XSLT for many years, we've found a few places where the self:: axis is really a lifesaver. The biggest example is when you want to exclude a particular node when selecting a nodeset. For instance, let's say that you have input XML in a format like this:

<line>
<text>This is a line of text to be printed</text>
<fontsize>14</fontsize>
<justification>Center</justification>
...
</line>

The idea is that the <text> element is the only one that's required, and there are a bunch of optional tags you can add to change that Line from the default.

The problem we're trying to solve is that if there are any of these optional tags present, we want to send the user to the
"advanced" edit page, whereas if there are just lines of text without fancy
formatting on a particular line, we just want to show the "normal" edit page.

So our mission is to write a select statement that sees if there are any <line>s with any child elements other than <text>. The first thing that might come to your mind is something like this:

<xsl:variable name="AdvancedNodes" select="line/*[not(name() = 'text')]" />

A statement like this kind of works, and it'd get the job done, but doing comparisons based on the name seems kind of sloppy. It also gets a lot more tricky if you're using XML Namespaces, because then you need to check the local-name() and namespace-uri(). So, the much more elegant approach is something like this:

<xsl:variable name="AdvancedNodes" select="line/*[not(self::text)]" />

In this manner, the self:: axis lets you start with a very broad selection of elements (all children elements) and then narrow it down further very easily. You can also use techniques like this if you're writing XSLT that primarily copies elements, but you want to exclude some of the elements in the copy.

No comments: