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/">- Source: /code/beautiful-xml-with-xsl/atom-top.txt
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>- Source: /code/beautiful-xml-with-xsl/my-first-xsl.txt
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>- Source: /code/beautiful-xml-with-xsl/apply-template.txt
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>- Source: /code/beautiful-xml-with-xsl/atom-feed.txt
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>- Source: /code/beautiful-xml-with-xsl/recursion.txt
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>- Source: /code/beautiful-xml-with-xsl/twists.txt
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>- Source: /code/beautiful-xml-with-xsl/style.txt
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
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.
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)?
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”.
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.
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.
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.
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.
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…
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.
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.
17/12/2006
+1 for Symphony.
XSL actually works. never again will i touch wordpress/textpattern!
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. :)
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 :-)
Impress us