20th January 2010
At Headscape we use our own in house CMS. This technology is based on
There is however, no reason why a proficient CSS user can't take advantage of, and work with XSLT. In fact, if the XSL is written in a more declarative, rather than procedural manner it can be very similar to working with CSS to style HTML.
XSL is a stylesheet language that can be used to 'transform' (the 'T' in XSLT) XML into some other output. I can go from:
<item> <name nick="Craig">Craig Rowe</name> <company>Headscape</company> <group>colleague</group> </item>
to:
<h3>Contacts</h3> <p class="colleague"> <strong>Craig Rowe</strong> (Craig) from Headscape </p>
Although the transformation is from one text ouput to another we can see the similarities to applying CSS to HTML. With CSS the HTML starts off in one form, default browser styling renders it in a particular way (with fonts, padding, positioning), user CSS is then applied turning it into a slightly different visual output (different fonts, padding, positioning etc). In the XSL example above the only difference is that the final outcome is another type of text output rather than a graphical display.
In terms of the process we can imagine:
Backend Coder → generates → XML → transformed by → XSL → into → XHTML → styled by → CSS → ending in a → Beautifully designed website
The neat separation between content and presentation is immediately clear. If the 'engine' of an application created, used and output XML, then XSLT could be used to create any output without the need to modify a line of code. In fact this is how cargowire is created (as I've pointed to briefly before here and here). Multiple outputs are provided purely by leveraging XML and XSLT.
XSL, as a templating engine, is also backend language agnostic. That is to say I could go from running my site on .NET to running my site on PHP or Ruby but maintain the same XSL as long as I could generate the same semantic XML.
There are other benefits too... Many web APIs provide XML or RSS based outputs. Both of which can be transformed into HTML using XSL.
And it's not just us that use XSL. For example, Symphony CMS is a PHP based CMS solution that leverages XSL as its templating engine.
Both CSS and XSL are tasked with selecting particular elements within a document and applying some kind of property or change to them. Let's compare the differing selection syntax. The following are equivalent:
CSS Selectors | XSL Selectors (known as 'XPath') | Plain English | |
---|---|---|---|
1 | div#content p.intro | //div[@id='content']//p[@class='intro'] | p with class 'intro' inside div with id 'content' |
2 | #container thead th | //*[@id='container']//thead//th | th inside thead inside any element with id 'container' |
3 | #content h3 | //*[@id='content']//h3 | h3 inside any element with id 'content' |
4 | #content > h3 | //*[@id='content']/h3 | h3 as direct child of element with id 'content' |
5 | #content, .content | //*[@id='content'] | //*[@class='content'] | Any element with id 'content' or class 'content' |
6 | ul.inline li:last-child | //ul[@class='inline']/li[position()=last()]] | The last li inside a ul with class 'inline' |
7 | //a/@href | Select the href attribute of all anchor elements | |
8 | . | The current context node | |
If you want to test out these XPath selectors try the Firefox XPather plugin which allows you to type XPath selectors in a console window and see what results are returned from your html.
The template below could be used to transform the example data above. Do not be put off, it is very HTML esque with some opening tags at the top and a few document type declarations like xml version and output method, with the real meat in the centre.
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" /> <xsl:template match="/"><!-- Will match the root of the document and therefore run first --> <xsl:call-template name="contacts" /><!-- Calls a template to run by name --> </xsl:template> <!-- A named template --> <xsl:template name="contacts"> <h3>Contacts</h3> <xsl:apply-templates select="//item" /><!-- Apply all rules/templates that match the selector --> </xsl:template> <!-- A match/rule based template --> <xsl:template match="//item"> <p><!-- Attributes can be added as below or using curly brace shorthand e.g. class="{group}" --> <xsl:attribute name="class"> <xsl:value-of select="group"/> </xsl:attribute> <strong> <xsl:value-of select="name"/> </strong> <xsl:text> (</xsl:text> <xsl:value-of select="name/@nick"/> <xsl:text>) from </xsl:text><xsl:value-of select="company"/> </p> </xsl:template> </xsl:stylesheet>
Like HTML, XSL files start and end with an angled bracket element (<xsl:stylesheet>) and can contain any number of templates, which themselves will contain any number of statements including plain text output. Any <xsl:...> tags are part of the stylesheet and not output. Any other elements, such as the H3 seen above, will be output as is.
When this template is applied to XML the following will happen:
As mentioned earlier and discussed in the notes below XSL stylesheets can have a distinctly procedural feel to them or a more declarative. In the template above we get a flavour of both. The more procedural style occurs where a template is called by name (line 6) - akin to a javascript method being called that document.write()'s. The more declarative style can be seen where templates are set up to 'match' a selector statement (line 13 and 18) with apply-templates being called to tell the xsl processor to match those rules.
As a developer I mainly use XSLT from within a backend language (in my case, .NET which supports XSL 1.0) however many browsers are also XML/XSL aware so it is quite easy to test locally without any backend code knowledge simply by adding the reference to the stylesheet in the xml itself.
<?xml-stylesheet type="text/xsl" href="items.xsl"?>
For an example of this please see items.xml and items.xsl (using view source to see the xml and xslt that is used). If you are using an XSL aware browser when you hit items.xml you should see the transformed html (which can be seen in firebug - view source will show the raw xml).
Using this technique the template generation can be done locally with example XML data before the intended application is even written. This can be used in conjunction with XPather for debugging.
A more elaborate example can be garned from Cargowire itself. Last week I wrote about how Yahoo Pipes can be used to create an aggregate and filtered RSS feed. The output was a feed for the Headscape bloggers. On it's own this is useful for subscriptions however with XSLT we are able to present the content in an entirely different way.
The data source is standard RSS which we know is XML and can be transformed as such. A trimmed down version of the XSL for the barn page can be seen below. This transform occurs on the server side after code has retrieved the feed from Yahoo and before it sends a response to the user.
<xsl:template match="//page/*/*/channel"> <!-- Assumes a root matching template somewhere else in the XSL as well as the definition of the gravatar template and $formatstring variable --> <p>This is simply a combined feed from <a href="http://www.boagworld.com" title="boagworld.com">Paul</a>, <a href="http://www.edmerritt.com" title="edmerritt.com">Ed</a>, <a href="http://www.davemcdermid.co.uk" title="davemcdermid.co.uk">Dave</a>, <a href="http://www.robborley.com" title="robborley.com">Rob</a> and <a href="/about#stalk">Myself</a></p> <div id="publicationLinks"> <dl id="publication"> <xsl:for-each select="item"> <dt> <h3> <xsl:choose> <xsl:when test="string-length(link) > 0"> <a href="{link}"> <!-- if this is an external link mark it as so --> <xsl:if test="not(starts-with(link, 'http://cargowire'))"> <xsl:attribute name="rel">external</xsl:attribute> </xsl:if> <xsl:value-of select="title" /> </a> </xsl:when> <xsl:otherwise> <xsl:value-of select="title" /> </xsl:otherwise> </xsl:choose> <!-- A call to a template to output the avatar image --> <xsl:call-template name="Gravatar"/> </h3> </dt> <dd> <!-- call into some code to format the date after creating a variable --> <xsl:variable name="datetimestampformat"> <xsl:text>yyyy-MM-ddTHH:mm:ss+00:00</xsl:text> </xsl:variable> <!-- use curly brace shorthand to use xsl/xpath to set an attribute value --> <p class="published" title="{date:FormatDate(string(pubDate), $datetimestampformat)}"> <xsl:value-of select="date:FormatDate(string(pubDate),$formatString)" /> </p> <p class="abstract"> <!-- Disable output escaping will maintain html tags in the output --> <xsl:value-of select="description" disable-output-escaping="yes" /> </p> <xsl:if test="string-length(link) > 0"> <a href="{link}" class="moreLink"> <xsl:if test="not(starts-with(link, 'http://cargowire'))"> <xsl:attribute name="rel">external</xsl:attribute> </xsl:if> <xsl:text>Read More...</xsl:text> </a> </xsl:if> </dd> </xsl:for-each> </dl> </div> </xsl:template>
In the above stylesheet snippet we can see a template that matches the RSS channel element then loops through the items to produce an HTML based listing of the feed contents. Relatively self-describing constructs such as 'choose' and 'if' are used and so too are a couple of extras such as the call out to an external code object to format dates on line 33 and the use of not() and starts-with() on line 42. A variable is also defined on line 29 before being used on line 33.
If you want to find out more a good quick reference/start point for xslt 1.0 xpath and functions is provided by Mulberry Technologies.
This article aimed to give you a taste of XSL and how it is used. As such we have kept to quite minimal templates and selectors however these core aspects of XSL should allow you to view and make ammendments to XSL templates without too much trouble. By using tools such as XPather and XSL in the browser you should be well equipped to make changes to and understand many XSL templates created by backend developers. It's also important to remember that although this example has discussed tranforming to HTML the output from XSL could just have equally been json, rss or even plain text.
The final example from Cargowire aims to illustrate a practical example and as such contains some more complicated functionality that is not fully described in this article. However if you are comfortable with HTML, CSS and perhaps a little javascript it should be relatively easy to follow.
In XPath there are three main ways to start a selector:
In terms of the 'current context': If I was inside a template that had just matched 'item' an XPath of 'seconditem' would look for a direct child of the 'item' element that is named 'seconditem'. It would not match all 'seconditem' instances in the entire xml document.
In terms of CSS this could be seen as a rule that only applies to elements inside another rule. Or in jQuery a selector that runs from the 'this' context e.g jQuery('seconditem', this).
back - back to topIt is my experience that many backend developers who move in to using XSL are keen to maintain the traditional coding paradigm of one thing following another until completion. Taking this view with XSL can lead to single large primary templates with many conditionals, loops and calling of sub templates.
This can make XSL difficult to read, particularly to non-technical users. However XSL is in its element when used declaratively. Instead of attempting to run down the XML as if parsing it in code simply define a number of rules that match the relevant parts and then apply those rules to the XML document (like CSS).
Lets look at a simple example comparison between two templates that do the same thing:
<addressbook> <name>Work</name> <items> <item> <name nick="Craig">Craig Rowe</name> <company>Headscape</company> </item> <item> <name nick="Craig">Craig Rowe</name> <company>Headscape</company> </item> </items> </addressbook>
Procedural Style | Declarative Style |
---|---|
<xsl:templates match="/"> <h2><xsl:value-of select="name"/></h2> <xsl:for-each select="items/item"> <h3><xsl:value-of select="@nick" /></h3> </xsl:for-each> </xsl:template> This style has the look of javascript structure where loops and tests occur in sequence |
<xsl:templates match="/"> <xsl:apply-templates select="items/name" /> <xsl:apply-templates select="items/item" /> </xsl:template> <xsl:templates match="name"> <h2><xsl:value-of select="."/></h2> </xsl:templates> <xsl:template match="item"> <h3><xsl:value-of select="@nick" /></h3> </xsl:template> This style has the look of a CSS structure i.e. a list of rules that match selectors |
All article content is licenced under a Creative Commons Attribution-Noncommercial Licence.