XSL for XML with namespaces
This started off as a problem with writing a single XSL for RSS and ATOM feeds.
To be fair, producing separate XSL files for RSS and ATOM feeds should not be a huge problem.
Attempting to merge them is probably not trivial.
Thankfully, writing a XSL file for RSS is pretty trivial. It uses regular XSL know-how.
However, writing one for ATOM seemed very different!
The reason is that the ATOM XML comes with namespaces.
I've encountered these two namespaces in use in different feeds:
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
and
<feed xmlns="http://www.w3.org/2005/Atom">
As such, a regular template match will not match the content of the document.
<xsl:template match="feed">
I'll need to write the XSL such that both gets matched.
In order to match the first feed, I'll need the template to be:
<xsl:template match="{http://purl.org/atom/ns#}feed">
To shortcut this, you'll need to have a prefix in your XSLT stylesheet that is associated with the namespace.
E.g.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://purl.org/atom/ns#"
xmlns:atom1="http://www.w3.org/2005/Atom">
The template match will now be:
<xsl:template match="atom:feed">
As well as this:
<xsl:template match="atom1:feed">
And both together:
<xsl:template match="atom:feed atom1:feed">
This is definitely painful since I'll always be matching against two different namespaces for ATOM feeds!
A better solution is to have the template match ignore the namespace in the source XML.
The following will match the ATOM root element regardless of the namespace:
<xsl:template match="*[local-name()='feed']">
Therefore, the following will match the RSS and ATOM's base elements:
<xsl:template match="channel *[local-name()='feed']">
Problem has largely been solved!
The following is an experimental XSL (PS: I only handle single channels for RSS)
To be fair, producing separate XSL files for RSS and ATOM feeds should not be a huge problem.
Attempting to merge them is probably not trivial.
Thankfully, writing a XSL file for RSS is pretty trivial. It uses regular XSL know-how.
However, writing one for ATOM seemed very different!
The reason is that the ATOM XML comes with namespaces.
I've encountered these two namespaces in use in different feeds:
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
and
<feed xmlns="http://www.w3.org/2005/Atom">
As such, a regular template match will not match the content of the document.
<xsl:template match="feed">
I'll need to write the XSL such that both gets matched.
In order to match the first feed, I'll need the template to be:
<xsl:template match="{http://purl.org/atom/ns#}feed">
To shortcut this, you'll need to have a prefix in your XSLT stylesheet that is associated with the namespace.
E.g.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://purl.org/atom/ns#"
xmlns:atom1="http://www.w3.org/2005/Atom">
The template match will now be:
<xsl:template match="atom:feed">
As well as this:
<xsl:template match="atom1:feed">
And both together:
<xsl:template match="atom:feed atom1:feed">
This is definitely painful since I'll always be matching against two different namespaces for ATOM feeds!
A better solution is to have the template match ignore the namespace in the source XML.
The following will match the ATOM root element regardless of the namespace:
<xsl:template match="*[local-name()='feed']">
Therefore, the following will match the RSS and ATOM's base elements:
<xsl:template match="channel *[local-name()='feed']">
Problem has largely been solved!
The following is an experimental XSL (PS: I only handle single channels for RSS)
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="text()" priority="-1">
</xsl:template>
<!-- matches both rss and atom -->
<xsl:template match="channel *[local-name()='feed']">
<html>
<body>
<h3>
<xsl:choose>
<xsl:when test="name(.) = 'channel'">
<a href="{link}" target="_blank"><xsl:value-of select="title"/></a>
</xsl:when>
<xsl:otherwise>
<a href="{*[local-name()='link']/@href}" target="_blank"><xsl:value-of
select="*[local-name()='title']"/></a>
</xsl:otherwise>
</xsl:choose>
</h3>
<table cellpadding="1" cellspacing="1" width="95%">
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<!-- matches both rss and atom -->
<xsl:template match="item *[local-name()='entry']">
<xsl:apply-templates/>
</xsl:template>
<!-- matches rss -->
<xsl:template match="item/title">
<tr>
<td bgcolor="#AAAAAA">
<xsl:number count="item"/>. <a
href="{../link}" target="_blank"><xsl:value-of select="."/></a>
</td>
</tr>
</xsl:template>
<!-- matches atom -->
<xsl:template match="*[local-name()='entry']/*[local-name()='title']">
<tr>
<td bgcolor="#AAAAAA">
<xsl:number count="*[local-name()='entry']"/>. <a
href="{../*[local-name() = 'link']/@href}" target="_blank"><xsl:value-of select="."/></a>
</td>
</tr>
</xsl:template>
<!-- matches rss -->
<xsl:template match="item/description">
<tr>
<td bgcolor="#EEEEEE">
<xsl:value-of select="." disable-output-escaping="no"/>
<br/><br/>
</td>
</tr>
</xsl:template>
<!-- matches atom -->
<xsl:template match="*[local-name()='entry']/*[local-name()='content']">
<tr>
<td bgcolor="#EEEEEE">
<xsl:value-of select="." disable-output-escaping="no"/>
<br/><br/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Comments