Wednesday, February 07, 2007

Stupid XSLT Trick #1: Escape Madness

XSLT is a language that has caused endless amounts of teeth gnashing and wailing. When you combine xslt with some of the spectacularly bad parts of RSS that makes people gnash their teeth and scream you end up with toothless programmers condemned to speak in whispers due to damaged vocal cords.

But what if I said there was a way out of the darkness, a quick and dirty hack that offered salvation from eating pudding for the rest of your life?

The following code takes advantage of the ability of XSLT to have "modes". If you attended a CS course on the fundamentals of Computer Science and managed to stay awake, you'll remember your professor mentioning Turing machines. These hypothetical machines travel along a series of 1s and 0s in various states. For example, If it's in state A and sees a one, it might change to state B and go forward two numbers. You can have just the same amount of fun with XSLT.

Seriously though, it allows us to traverse an unpredictable tree and specify behavior in a concise way.

So here's the code:

<xsl:template match="/">
<someAlmostMarkup>
<xsl:apply-templates mode="escape" />
</someAlmostMarkup>
</xsl:template>

<xsl:template match="*" mode="escape">
<xsl:text>&lt;</xsl:text>
<xsl:value-of select="name()" />
<xsl:apply-templates mode="escape" select="@*" />
<xsl:text>&gt;</xsl:text>
<xsl:apply-templates mode="escape" />
<xsl:text>&lt;/</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>&gt;</xsl:text>
</xsl:template>

<xsl:template match="@*" mode="escape">
<xsl:text> </xsl:text>
<xsl:value-of select="name()" />
<xsl:text>="</xsl:text>
<xsl:value-of select="." />
<xsl:text>"</xsl:text>
</xsl:template>

And even an example xml doc

<songlist>
<song>
<Artist status="boozed" born="1-27-1913">J-Live</Artist>
<Genre>Rap</Genre>
</song>
<song>
<Artist status="dead">Phish</Artist>
<Genre>Rock</Genre>
</song>
<song>
<Artist status="energy" born="1-1-812">Radical From Planet G</Artist>
<Genre>Rap</Genre>
</song>
<song>
<Artist status="rocking">Queen</Artist>
<Genre>Rock</Genre>
</song>
</songlist>

In puesdo-xslt, this is saying

for each element you come across, print :
&lt; the name of current element (Apply templates to current element's attributes) &gt; (Apply templates current element's children elements and text nodes) &lt;/ the name of current element >

For each attribute print name="value"

I'm relying on the default template for text nodes which is to simply print them out as well as the default behavior of an which is to select both element nodes and text nodes.


Notice you'll want to change match="/" with something else, probably foo.

Of course, most people building up an rss feed won't want to just stick an escaped element in there. Well, remember, you're just sending out text. That means you can't use all those cool nifty things xslt can do with xml. But you can always write something that looks like an element by using &lt; and the name() function.

Still don't like it? Complain to the RSS spec designers.

2 comments:

Jeremy said...

Very cool (and funny). RSS is so sucky to produce and consume. This is definitely part of my XSLT library now. Thanks for the tip!

So, next thing: have you got a cool way to convert all those escaped characters in someone else's feed back into the elements they're meant to represent, and treat these as XML nodes? ;-)

Jeff said...

I added another template to cover comments in the source:


<!--

-->


You may have to work to make sure you select comments if you put a select="*|comment()" in the first apply templates, it should do the trick.