Transforming XML into HTML with XSLT
XSLT is short for XML Stylesheet Transformations, and it's a language for converting XML documents into other formats--usually another XML dialect, but it can output plain text as well. The popular use of XSLT is to directly convert XML into HTML, and it's valuable because you've got complete control over how that HTML looks, making it possible to build an XSLT "template" that will spit out web pages matching your site's look and feel.
There's an increasing number of reasons to want a quick and easy way to convert XML into HTML, especially when you can tune it to the cosmetics and layout of your site. For example, Amazon's affiliate program provides a way to query their catalog for products
To begin, there are two Perl modules we need:
- XML::LibXML
- XML::LibXSLT
These can both be installed from CPAN. From the command line of your server you'd type:
perl -MCPAN -e 'install XML::LibXML' perl -MCPAN -e 'install XML::LibXSLT'
The first, LibXML, gives us an easy way to parse the two XML documents (the source, and the XSLT stylesheet). The second, LibXSLT, applies the rules of the stylesheet to the XML.
To use these two modules we'll need to create a Global UserTag, or a tag that Interchange will load at startup, before it loads any catalogs. This will give it enough permission to load and execute code from external modules. We begin by creating a new file in your Interchange installation directory, not the catalog's directory. In recent versions of Interchange, there's a convenient place set aside for Global UserTags which you'll find in code/UserTag. Just by dropping a file in here with the .tag extension, Interchange will load it automatically on its next restart.
Next, paste the following code into the file, which we'll call xslt_transform.tag.
UserTag xslt-transform addAttr
UserTag xslt-transform Routine <<EOR
use XML::LibXSLT;
use XML::LibXML;
use LWP::Simple;
sub transform_output {
my ($source, $style) = @_;
my $xslt = XML::LibXSLT->new();
my $parser = XML::LibXML->new();
my $style_doc = $parser->parse_file($style);
my $stylesheet = $xslt->parse_stylesheet($style_doc);
my $results = $stylesheet->transform($source);
return $stylesheet->output_string($results);
}
sub {
my $opt = shift;
my $xml = $opt->{xml};
if ($opt->{xml_url}) {
$xml = LWP::Simple::get($opt->{xml_url});
}
my $parser = XML::LibXML->new();
my $source = $parser->parse_string($xml);
return transform_output($source,$opt->{xslt});
}
EOR
In this UserTag I'm also using LWP::Simple so we can support two options for passing the XML source: as a string, or as a URL that the tag can fetch for you.
XSLT-TRANSFORM: Synopsis
[xslt-transform xml_url="http://www.example.com/products.xml" xslt="xslt/products_2_html.xslt"]
Assuming there's an XML file at http://www.example.com/products.xml, the tag will retrieve it and transform it into HTML using a stylesheet called products_2_html.xslt that it finds in your catalog's xslt directory.
Examples
We'll cook the simplest possible XML document we can to show how XSLT works.
<?xml version="1.0"?> <products> <product> <name>Widgetmaster</name> <price>19.95</price> </product> <product> <name>Widget Accessory</name> <price>4.95</price> </product> </products>
An XSLT stylesheet works by defining a collection of templates, each template works on a specific XML tag or--to be more precise--an XML tag pattern. In our example, product is one of the XML tags, while product/price is a pattern that means "any price that's found within a product". As the XML file is being processed, the interpreter steps through each tag until it finds one that matches a template.
The following XSLT stylesheet will gobble up the above XML and spit out a fragment of HTML that you can insert anywhere in your page.
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:template match="products">
<h2>Products</h2>
<ul>
<xsl:apply-templates select="product"/>
</ul>
</xsl:template>
<xsl:template match="products/product">
<li><xsl:value-of select="name"/>
- <xsl:apply-templates select="price"/></li>
</xsl:template>
<xsl:template match="price">
$<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Step 1: The header and closing tag
The header just starts the stylesheet and provides some namespace information. Since all XSLT stylesheets are also XML documents, the root element <xsl:stylesheet> must have a closing tag at the end of the file.
Step 2: The root template
The first template matches against the products tag, which we're expecting to be the root element of the XML document we're processing. It's from here that all the other templates get called (or implied) from. This makes it the perfect place to display any titles and set up any enclosing structures, such as an Unordered List (ul tags) in our example.
Step 3: The product template
We want each product to be listed as a List Item (li tag) within our Unordered List, so we create a template that matches on the product tag and make it start and end with an li tag. Within this, we tell the stylesheet to get the "value-of" the name tag, followed by a command to apply any available templates to the price tag.
Step 4: Styling individual elements
We want to display the price with a dollar sign preceding it. We could have done it this way:
<xsl:template match="products/product"> <li><xsl:value-of select="name"/> - $<xsl:value-of select="price"/></li> </xsl:template>
The result would have been exactly the same. But what if the author of the XML document decides to adapt it for other currencies? He solves his problem by adding a currency parameter to the price tag, so it looks like this:
<price currency="euro">4.95</price>
Suddenly our dollar sign is inappropriate. It would be better if we left the currency symbol to another template to decide. XSLT makes this easy for us, because we can just throw in another template that matches a more specific pattern, like this:
<xsl:template match="product/price[@currency='euro']"> €<xsl:apply-templates/> </xsl:template>
The magic is the bit we added to the end of the pattern: [@currency='euro']. The "@" symbol denotes a tag parameter, while a forward slash (/) denotes a child tag. XSLT prefers to apply the template with the most specific match possible, so we don't have to make any changes to our existing price template. This is where the real power of XSLT starts to show.
Being able to specify what the parent element must be is also useful. Right now, our price tag matches only when it's a child of a product tag. But what if our XML document was expanded again to include services this time?
<service> <name>Window washing</name> <price>29.95</price> </service>
Because services are sold at hourly rates, we want to show that the price is per hour. To do this, add a new template that looks like this:
<xsl:template match="service/price"> $<xsl:apply-templates/>/hour </xsl:template>
To make sure products and services get listed together, we can go up and modify the match terms of the first two templates to include them, using the pipe ( | ) operator, which means "or".
<xsl:template match="products"> <h2>Products</h2> <ul> <xsl:apply-templates select="product | service"/> </ul> </xsl:template> <xsl:template match="products/product | products/service"> <li><xsl:value-of select="name"/> - <xsl:apply-templates select="price"/></li> </xsl:template>
The output
After running the XML through our UserTag, you can expect an output like this one:
Products
- Widgetmaster - $19.95
- Widget Accessory - $4.95
- Euromaster - €24.95
- Window washing - $29.95/hour

Leave a Reply