Rendering XML as valid RSS with XSLT

I’ve been using a simple XML file to store updates to the main duncanandmeg.org website, and wanted to use XSL to transform that data into a valid RSS feed. This took a little more work than most of my XSL templates to date, so I thought I’d log the details here.

XML data

The following is the structure of my XML file. I’ve been storing dates in the file in short date format. The detail element is optional, and since is the only element where I embed raw HTML, I encapsulate the contents in CDATA elements.
Also, since I’m updating this file manually at this point, I add new entries to the top of the file rather than worry about using more automated date sorting.

<news>
  <entry>
    <headline>headline</headline>
     <date>mm/dd/yy</date>
    <detail><![CDATA[more details (if applicable)]]></detail>
  </entry>
</news>

XSL transform

Because RSS is a more complex standard than my simple XML file, the XSL transform is necessarily complex.

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml"/>

  <xsl:template match="/news">
  <rss version="2.0">
    <channel>
      <title>hard-coded title</title>
      <link>hard-coded url</link>
      <description>hard-coded description</description>
      <ttl>60</ttl>
      <xsl:apply-templates select="entry[position() < 6]" />
    </channel>
  </rss>
  </xsl:template>

  <xsl:template match="entry">
    <item>
      <title><xsl:value-of select="headline" /></title>
      <link><xsl:text>http://www.duncanandmeg.org/news.php#</xsl:text>
        <xsl:value-of select="position()" /></link>
      <guid><xsl:text>http://www.duncanandmeg.org/news.php#</xsl:text>
        <xsl:value-of select="position()" /></guid>
      <pubDate><xsl:value-of select="date" /></pubDate>
      <description>
        <xsl:if test="detail">
          <xsl:value-of disable-output-escaping="no" select="detail" />
        </xsl:if>
      </description>
    </item>
   </xsl:template>

</xsl:stylesheet>

This produces almost valid RSS feed for the top 5 items in the feed.

The generated RSS includes dates in short date format (mm/dd/yy), which violates the RSS standard which calls for the RFC-822 date-time format. Since all the implementations I could find on the web for doing date conversions in XSL were far too complex for my taste, I proceeded to do that with PHP when I actually transform the XML file into RSS.

PHP code

The following PHP represents a minimalist approach to executing the XSL transform and returning RSS as output. This script doesn’t handle caching, and there are issues with the <link> and <guid> elements in the template for each <entry>.

The consequence of these weaknesses is that some feed readers don’t properly update when a new post is added to the news.xml file. This is something I’ll perhaps address in another post.

&lt;?php
  //Optional. Sets default time zone to something other than GMT
  date_default_timezone_set('EST');

  //Load XML data and transform to RSS
  $xslt = new xsltprocessor;
  $xslt->importStyleSheet(DomDocument::load('rss.xsl'));
  $xmlResult = $xslt->transformToXML(DomDocument::load('news.xml'));

  //Load resulting data into the SimpleXML processor
  $xml=simplexml_load_string($xmlResult);

  //Loop through RSS data in SimpleXML
  //Replace each short date with an RFC-822 date-time
  $count = 0;
  foreach($xml->xpath('//pubDate') as $dates){
    $newDates = date(DATE_RFC822, strtotime($dates));
    $xml->channel->item[$count]->pubDate = $newDates;
    $count++;
  }

  //Optional. These lines insert RFC-822 values
  //for channel/lastBuildDate and channel/pubDate
  $xml->channel->addChild("lastBuildDate",$xml->channel->item[0]->pubDate);
  $xml->channel->addChild("pubDate",$xml->channel->item[0]->pubDate);

  //Set output content-type to XML
  header('Content-type: application/rss+xml; charset=utf-8');
  print $xml->asXML(); //Return as XML
?&gt;

2 Responses to “Rendering XML as valid RSS with XSLT”


Comments are currently closed.