Remember that first time you saw XML and got it? When you really understood what was possible and the deep meaning each element could carry? Now when you see XML, it looks ugly, especially when you navigate to a page of XML in a browser. Well, with every modern browser now supporting XSL 1.0, I’m going to show you how you can turn something as simple as an ATOM feed into a customised page using a browser, Notepad and some XSL.
What on earth is this XSL?
XSL is a family of recommendations for defining XML document transformation and presentation. It consists of three parts:
- XSLT 1.0 – Extensible Stylesheet Language Transformation, a language for transforming XML
- XPath 1.0 – XML Path Language, an expression language used by XSLT to access or refer to parts of an XML document. (XPath is also used by the XML Linking specification)
- XSL-FO 1.0 – Extensible Stylesheet Language Formatting Objects, an XML vocabulary for specifying formatting semantics
XSL transformations are usually a one-to-one transformation, but with newer versions (XSL 1.1 and XSL 2.0) its possible to create many-to-many transformations too. So now you have an overview of XSL, on with the show…
So what do I need?
So to get going you need a browser an supports client-side XSL transformations such as Firefox, Safari, Opera or Internet Explorer. Second, you need a source XML file – for this we’re going to use an ATOM feed from Flickr.com. And lastly, you need an editor of some kind. I find Notepad++ quick for short XSLs, while I tend to use XMLSpy or Oxygen for complex XSL work.
Because we’re doing a client-side transformation, we need to modify the XML file to tell it where to find our yet-to-be-written XSL file. Take a look at the source XML file, which originates from my Flickr photos tagged sky, in ATOM format.
The top of the ATOM file now has an additional <?xml-stylesheet /> instruction, as can been seen on Line 2 below. This instructs the browser to use the XSL file to transform the document.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="flickr_transform.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/">
Your first transformation
Your first XSL will look something like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<xsl:output method="html" encoding="utf-8"/>
</xsl:stylesheet>
This is pretty much the starting point for most XSL files. You will notice the standard XML processing instruction at the top of the file (line 1). We then switch into XSL mode using the XSL namespace on all XSL elements (line 2). In this case, we have added namespaces for ATOM (line 4) and Dublin Core (line 5). This means the XSL can now read and understand those elements from the source XML.
After we define all the namespaces, we then move onto the xsl:output element (line 6). This enables you to define the final method of output. Here we’re specifying html, but you could equally use XML or Text, for example. The encoding attributes on each element do what they say on the tin. As with all XML, of course, we close every element including the root.
The next stage is to add a template, in this case an <xsl:template /> as can be seen below:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<xsl:output method="html" encoding="utf-8"/>
<xsl:template match="/">
<html>
<head>
<title>Making XML beautiful again : Transforming ATOM</title>
</head>
<body>
<xsl:apply-templates select="/atom:feed"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The beautiful thing about XSL is its English syntax, if you say it out loud it tends to make sense.
The / value for the match attribute on line 8 is our first example of XPath syntax. The expression / matches any element – so this <xsl:template/> will match against any element in the document. As the first element in any XML document is the root element, this will be the one matched and processed first.
Once we get past our standard start of a HTML document, the only instruction remaining in this <xsl:template/> is to look for and match all <atom:feed/> elements using the <xsl:apply-templates/> in line 14, above.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<xsl:output method="html" encoding="utf-8"/>
<xsl:template match="/">
<xsl:apply-templates select="/atom:feed"/>
</xsl:template>
<xsl:template match="/atom:feed">
<div id="content">
<h1>
<xsl:value-of select="atom:title"/>
</h1>
<p>
<xsl:value-of select="atom:subtitle"/>
</p>
<ul id="entries">
<xsl:apply-templates select="atom:entry"/>
</ul>
</div>
</xsl:template>
</xsl:stylesheet>
This new template (line 12, above) matches <feed/> and starts to write the new HTML elements out to the output stream. The <xsl:value-of/> does exactly what you’d expect – it finds the value of the item specifed in its select attribute. With XPath you can select any element or attribute from the source XML.
The last part is a repeat of the now familiar <xsl:apply-templates/> from before, but this time we’re using it inside of a called template. Yep, XSL is full of recursion…
<xsl:template match="atom:entry">
<li class="entry">
<h2>
<a href="{atom:link/@href}">
<xsl:value-of select="atom:title"/>
</a>
</h2>
<p class="date">
(<xsl:value-of select="substring-before(atom:updated,'T')"/>)
</p>
<p class="content">
<xsl:value-of select="atom:content" disable-output-escaping="yes"/>
</p>
<xsl:apply-templates select="atom:category"/>
</li>
</xsl:template>
The <xsl:template/> which matches atom:entry (line 1) occurs every time there is a <entry/> element in the source XML file. So in total that is 20 times, this is naturally why XSLT is full of recursion. This <xsl:template/> has been matched and therefore called higher up in the document, so we can start writing list elements directly to the output stream. The first part is simply a <h2/> with a link wrapped within it (lines 3-7). We can select attributes using XPath using @.
The second part of this template selects the date, but performs a XPath string function on it. This means that we only get the date and not the time from the string (line 9). This is achieved by getting only the part of the string that exists before the T.
Regular Expressions are not part of the XPath 1.0 string functions, although XPath 2.0 does include them. Because of this, in XSL we tend to rely heavily on the available XML output.
The third part of the template (line 12) is a <xsl:value-of/> again, but this time we use an attribute of <xsl:value-of/> called disable output escaping to turn escaped characters back into XML.
The very last section is another <xsl:apply-template/> call, taking us three templates deep. Do not worry, it is not uncommon to write XSL which go 20 or more templates deep!
<xsl:template match="atom:category">
<xsl:for-each select=".">
<xsl:element name="a">
<xsl:attribute name="rel">
<xsl:text>tag</xsl:text>
</xsl:attribute>
<xsl:attribute name="href">
<xsl:value-of select="concat(@scheme, @term)"/>
</xsl:attribute>
<xsl:value-of select="@term"/>
</xsl:element>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
In our final <xsl:template/>, we see a combination of what we have done before with a couple of twists. Once we match atom:category we then count how many elements there are at that same level (line 2). The XPath . means ‘self’, so we count how many category elements are within the <entry/> element.
Following that, we start to output a link with a rel attribute of the predefined text, tag (lines 4-6). In XSL you can just type text, but results can end up with strange whitespace if you do (although there are ways to simply remove all whitespace).
The only new XPath function in this example is concat(), which simply combines what XPaths or text there might be in the brackets. We end the output for this tag with an actual tag name (line 10) and we add a space afterwards (line 12) so it won’t touch the next tag. (There are better ways to do this in XSL using the last() XPath function).
After that, we go back to the <xsl:for-each/> element again if there is another category element, otherwise we end the <xsl:for-each/> loop and end this <xsl:template/>.
A touch of style
Because we’re using recursion through our templates, you will find this is the end of the templates and the rest of the XML will be ignored by the parser. Finally, we can add our CSS to finish up. (I have created one for Flickr and another for News feeds)
<style type="text/css" media="screen">@import "flickr_overview.css?v=001";</style>
So we end up with a nice simple to understand but also quick to write XSL which can be used on ATOM Flickr feeds and ATOM News feeds. With a little playing around with XSL, you can make XML beautiful again.
All the files can be found in the zip file (14k)

Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
07/12/2006
It’s a shame you can’t see custom-designed feeds in Safari, Internet Explorer 7 nor Firefox 2. All this good work is going unnoticed.
Vote Helpful or Unhelpful
07/12/2006
..but how do i manage to insert the tag when i’m loading an external xml (such your flicker or, in my case, a bloglines rss)?
Vote Helpful or Unhelpful
07/12/2006
> The expression / matches any element
That’s not true. The expression / matches the document root, and nothing else. And by document root, I don’t mean the document element (atom:feed) but the actual root, the document itself. This is an explicit example in XSLT 1.0 Section 5.2 “Patternsâ€.
Vote Helpful or Unhelpful
07/12/2006
@trovster: at first I thought so, too. But at least in Safari you can turn off the RSS support (via the Debug menu) and look at the nice formatted feeds.
Vote Helpful or Unhelpful
07/12/2006
For client-side transformations I sometimes used the javascript Freja framework, that allows to work with more than one XSL file in the same time.
Vote Helpful or Unhelpful
07/12/2006
saski:
You don’t have to insert a “tag†(processing instruction). When your browser comes across the processing instruction it starts up an instance of its XSLT-Processor and passes in both the stylesheet and your XML-Document.
That is exactly what you have to do. The question is, which language do you use??
Check out http://php.net/xsl – it should demonstrate the concept.
Vote Helpful or Unhelpful
07/12/2006
I totally fell into XSL by using a Publishing tool called Symphony. The fact that the interface looked cool, the language was something that was recommended by the W3C and also it further separated chunks of code that could be reused in different scenarios totally appealed to me.
Vote Helpful or Unhelpful
08/12/2006
Sebastian is correct about / matching the root of the document. XPath is based on UNIX path semantics so it should be pretty obvious.
Personally, I don’t like splitting up my code into so many templates. It’s old school xslt imho and makes the markup difficult to follow. Use xsl:for-each instead. Calling templates by name and using param’s is incredibly useful for creating xslt’s equivalent of functions. You could, for example, create a css constant processor for Rachel’s article this way although it’d probably have to be done server side.
Hmmm. Worth looking into…
Vote Helpful or Unhelpful
10/12/2006
Kevin, interesting take – but I tend to stand on the other side of that particular fence. For me, template matching is the most beautiful concept in XSLT. It’s the default behaviour by design, I can never understand why anyone would want to tamper with it by littering their stylesheet with < xsl:for-each > and called templates when there’s no good reason to do so, although I see it all the time. There’s nothing more elegant to me than being able to match the root node, apply templates and just pick out what you need by matching, not that it’s ever that simple in the real world alas.
XSLT is immensely powerful, but if I found myself regularly attempting to replicate traditional programming concepts within it I’d start to question why I wasn’t using a traditional programming language in the first place.
Ian, nice article, it’s a subject that doesn’t come up often enough. I do have one issue though: Within the template matching the category element, the context node is a single instance of a category element. The < xsl:for-each select=â€.†> is therefore redundant as it will only ever match one category element – the current one. This template is already invoked independently for each category since you used < xsl:apply-templates select=“atom:category†> in the entry template, the < xsl:for-each > adds nothing but a slight processing overhead to each pass.
Vote Helpful or Unhelpful
12/12/2006
Sorry guys, I’ve been tied up recently and not had a chance to reply.
So in total agreement with Will about Templates. For-each’s will get you so far but if you want to keep things simple, neat and readable I would suggest Templates.
On with Will’s point
See this is the problem with XSL, its a great transformation language in need of a good pipeline framework around it. I’m using Cocoon for lots of my serious XSL building and it makes things so much quicker and I don’t ever have to worry about replicating programming concepts. Also XSL 2.0 now has things like regex and the ability to do many to many transformations without a pipeline framework like Cocoon. Yes you need Saxon 8+ but it comes on Java and .Net now, so there’s no excuse now.
Good point about processor overhead with my for-each, its a old habit of mine.
To the earlier points, I was tempted to put in a part about the difference between Client-side and Server-side transformations. Client-side is useful when you have control over the feed or use javascript but ideally server-side makes more sense for most usage.
Vote Helpful or Unhelpful
17/12/2006
+1 for Symphony.
XSL actually works. never again will i touch wordpress/textpattern!
Vote Helpful or Unhelpful
21/12/2006
@trovster:
Not really going unnoticed… The purpose of styling your feeds is to avoid that horrid error-like page most browsers show. If they display them nicely and on top of that ask you if you want to subscribe to it, all the better.
I’ve xsl-ed my feeds from day one on my blog for one reason alone. If someone not familiar with feeds click on them, they should be helped finding their way (ie, suggest feed readers, etc), not give them the XML and don’t care if they’re able to use it or not. :)
Vote Helpful or Unhelpful
09/01/2007
Have a look at http://feedme.mind-it.info/, it uses client side XSLT to transform an Atom feed into XHTML 1.1 for application/xhtml+xml compliant browsers (the rest gets XHTML 1.0 Strict). It uses some nifty real world Apache trickery to get things really working. All details are explained in the blog posting. The purpose here is to make the feed the website and vice versa because if you have a feed you have all the content you need :-)
Regarding feed behaviour of browsers not respecting the XSLT and delivering it straight to the browser as a feed there’s a workaround that I applied on my feedsite. Check the source code and you’ll find out how :-)
Vote Helpful or Unhelpful
22/02/2009
XSL and XSLT are the inventions of the demons. Worst programming idea ever. XML is a data structure, NOT a bed for procedural code. It’s clumsy, big, and incredibly limited. For those of you required to work with it, I pity you. For those who do so out of choice… you should probably go get your head examined.
Vote Helpful or Unhelpful
09/04/2009
You are indeed a beautiful person: worked 1st time right out of the box.
Vote Helpful or Unhelpful
Impress us