Thursday, March 6, 2008

Transforming feeds...one XSLT to rule them all

For a client at my work I needed to built a feed reading module in to their CMS. I created the following XSLT to transform the feeds to the desired HTML. This XSLT can be used for Atom 0.3, Atom 1.0, RSS 0.90, RSS 0.91, RSS 0.92, RSS 1.0 and RSS 2.0.

With some minor tweaks this can be used in Umbraco as well. Here is an example of reading a atom feed in Umbraco.


Here is my XSLT.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:atom3="http://purl.org/atom/ns#"
    xmlns:rss="http://purl.org/rss/1.0/"
    xmlns:rss09="http://my.netscape.com/rdf/simple/0.9/"    
    exclude-result-prefixes="xsl rdf dc rss rss09 atom atom3"
>

<!--
=============================================================================
 XSLT to convert RSS and Atom feeds to desired html
=============================================================================
-->
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>

<!--
=============================================================================
Input parameters for displaying maximum items from feed
=============================================================================
-->

<xsl:variable name="maxitems" select="10" />

<xsl:template match="/">
                
    <xsl:element name="ul">    
        <xsl:attribute name="class">rssfeed</xsl:attribute>        
        
        <!-- Atom 1.0 -->
        <xsl:apply-templates select="/atom:feed"/>
        <!-- Atom 0.3 -->
        <xsl:apply-templates select="/atom3:feed"/>
        <!-- RSS 0.91 , 0.92 , 2.0 -->
        <xsl:apply-templates select="rss/channel" />
        <!-- RSS 1.0 -->
        <xsl:if test="rdf:RDF/rss:item">
            <xsl:apply-templates select="rdf:RDF/rss:item[position() &lt; number($maxitems)+1]"/>
        </xsl:if>
        <!-- RSS 0.9 -->
        <xsl:if test="rdf:RDF/rss09:item">
            <xsl:apply-templates select="rdf:RDF/rss09:item[position() &lt; number($maxitems)+1]"/>
        </xsl:if>
        
    </xsl:element>

</xsl:template>


<!--
=============================================================================
 RSS 0.91,0.92,2.0
=============================================================================
-->
<xsl:template match="channel">        
    <xsl:apply-templates select="item[position() &lt; number($maxitems)+1]" />        
</xsl:template>

<xsl:template match="item">       
    <xsl:call-template name="drawlinks">
        <xsl:with-param name="item_link" select="link" />
        <xsl:with-param name="item_title" select="title" />
    </xsl:call-template>
</xsl:template>


<!--
=============================================================================
 RSS 1.0
=============================================================================
-->
<xsl:template match="rss:item">     
     <xsl:call-template name="drawlinks">
        <xsl:with-param name="item_link" select="rss:link" />
        <xsl:with-param name="item_title" select="rss:title" />
    </xsl:call-template>
</xsl:template>

<!--
=============================================================================
 RSS 0.90
=============================================================================
-->
<xsl:template match="rss09:item">    
    <xsl:call-template name="drawlinks">
        <xsl:with-param name="item_link" select="rss09:link" />
        <xsl:with-param name="item_title" select="rss09:title" />
    </xsl:call-template>
</xsl:template>

<!--
=============================================================================
Atom 1.0
=============================================================================
-->
<xsl:template match="/atom:feed">
    <xsl:apply-templates select="atom:entry[position() &lt; number($maxitems)+1]" />
</xsl:template>

<xsl:template match="atom:entry">
    <xsl:call-template name="drawlinks">
        <xsl:with-param name="item_link" select="atom:link/@href" />
        <xsl:with-param name="item_title" select="atom:title" />
    </xsl:call-template>
</xsl:template> 

<!--
=============================================================================
Atom 0.3
=============================================================================
--> 
<xsl:template match="/atom3:feed">    
    <xsl:apply-templates select="atom3:entry[position() &lt; number($maxitems)+1]" />    
</xsl:template>
 
<xsl:template match="atom3:entry">  
    <xsl:call-template name="drawlinks">
        <xsl:with-param name="item_link" select="atom3:link/@href" />
        <xsl:with-param name="item_title" select="atom3:title" />
    </xsl:call-template>
</xsl:template> 

<!--
=============================================================================
Template for making list items from feed items
=============================================================================
-->
<xsl:template name="drawlinks">
    <xsl:param name="item_link" />
    <xsl:param name="item_title" />
    <xsl:element name="li">        
        <xsl:element name="a">
            <xsl:attribute name="href"><xsl:value-of select="$item_link" /></xsl:attribute>
            <xsl:attribute name="target">_blank</xsl:attribute>
            <xsl:attribute name="title"><xsl:value-of select="$item_title" /></xsl:attribute>
            <xsl:value-of select="$item_title" />
        </xsl:element>    
    </xsl:element>
</xsl:template>
</xsl:stylesheet>

If you have issues with this, let me know.

Cheers,

Dave