ATOM's history tree with xslt

Building a hierarchical representation of an ATOM feed based on dates is challenging with XSLT.
Using "Muenchian Method" it's quite easy.
http://www.jenitennison.com/xslt/grouping/muenchian.html

The following xslt script takes an ATOM feed and creates a hierarchical xml grouping posts by years, months and day.

 

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <!--http://www.jenitennison.com/xslt/grouping/muenchian.html -->
   3: <xsl:stylesheet version="1.0"
   4:   xmlns:atom="http://www.w3.org/2005/Atom"
   5:   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   6:   xmlns:dc="http://purl.org/dc/elements/1.1/">
   7:   <xsl:output method="xml"/>
   8:   <xsl:template match="/">
   9:     <xsl:apply-templates select="/atom:feed"/>
  10:   </xsl:template>
  11:   <xsl:key name="year" match="atom:entry" use="substring(atom:published, 0, 5)" />
  12:   <xsl:key name="month" match="atom:entry" use="substring(atom:published, 0, 8)" />
  13:   <xsl:key name="day" match="atom:entry" use="substring(atom:published, 0, 11)" />
  14:   <xsl:template match="/atom:feed">
  15:     <FeedHierarchy>
  16:       <xsl:for-each select="atom:entry[count(. | key('year', substring(atom:published, 0, 5))[1]) = 1]">
  17:         <xsl:sort select="substring(atom:published, 6, 2)"  order="descending"/>
  18:         <year>
  19:           <xsl:attribute name="name">
  20:             <xsl:value-of select="substring(atom:published, 0, 5)" />
  21:           </xsl:attribute>
  22:  
  23:           <xsl:for-each select="key('year', substring(atom:published, 0, 5))[count(. | key('month', substring(atom:published, 0, 8))[1]) = 1]">
  24:             <xsl:sort select="substring(atom:published, 6, 2)" />
  25:             <month>
  26:               <xsl:attribute name="name">
  27:                 <xsl:value-of select="substring(atom:published, 6, 2)" />
  28:               </xsl:attribute>
  29:               <xsl:for-each select="key('month', substring(atom:published, 0, 8))[count(. | key('day', substring(atom:published, 0, 11))[1]) = 1]">
  30:                 <xsl:sort select="substring(atom:published, 0, 11)" />
  31:                 <day>
  32:                   <xsl:attribute name="name">
  33:                     <xsl:value-of select="substring(atom:published, 9, 2)" />
  34:                   </xsl:attribute>
  35:                   <!--<xsl:value-of select="atom:published" />,<br />-->
  36:                   <xsl:for-each select="key('day', substring(atom:published, 0, 11))">
  37:  
  38:                     <xsl:sort select="substring(atom:published, 9, 2)" />
  39:                     <entry>
  40:                       <a>
  41:                         <xsl:attribute name="href">
  42:                           <xsl:value-of select="atom:link[@rel='self']/@href" />
  43:                         </xsl:attribute>
  44:                         <xsl:value-of select="atom:title" />
  45:                       </a>
  46:                     </entry>
  47:                   </xsl:for-each>
  48:                 </day>
  49:               </xsl:for-each>
  50:             </month>
  51:  
  52:           </xsl:for-each>
  53:         </year>
  54:       </xsl:for-each>
  55:     </FeedHierarchy>
  56:   </xsl:template>
  57: </xsl:stylesheet>

The output XML includes only the title (line 44) and the link to the post 41.

I tested this with blogger.com feeds and not with other ATOM engines.

With the following ATOM (taken from http://buzz.blogger.com/atom.xml)

 

   1: <?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
   2: <feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
   3:   <id>tag:blogger.com,1999:blog-2399953</id>
   4:   <updated>2007-07-11T16:37:21.911-07:00</updated>
   5:   <title type='text'>Blogger Buzz</title>
   6:   <link rel='alternate' type='text/html' href='http://buzz.blogger.com/'/>
   7:   <link rel='next' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default?start-index=26&amp;max-results=25'/>
   8:   <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default'/>
   9:   <link rel='self' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default'/>
  10:   <author>
  11:     <name>Pete</name>
  12:   </author>
  13:   <generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
  14:   <openSearch:totalResults>316</openSearch:totalResults>
  15:   <openSearch:startIndex>1</openSearch:startIndex>
  16:   <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  17:   <entry>
  18:     <id>tag:blogger.com,1999:blog-2399953.post-5451556303142640543</id>
  19:     <published>2007-07-11T16:28:00.000-07:00</published>
  20:     <updated>2007-07-11T16:37:21.941-07:00</updated>
  21:     <category scheme='http://www.blogger.com/atom/ns#' term='feedburner'/>
  22:     <title type='text'>Attention FeedBurner Fans</title>
  23:     <content type='html'>&lt;a href="http://www.feedburner.com/"&gt;FeedBurner&lt;/a&gt; users - we just added a feature for you. Matt's &lt;a href="http://blogs.feedburner.com/feedburner/archives/2007/07/feedburner_integration_for_blo.php"&gt;post on the FeedBurner blog &lt;/a&gt;explains it more fully, but in short, you can now specify a URL to which Blogger will redirect your feed's traffic. This means you'll no longer have some subscribers getting your Blogger-hosted feed while others get your FeedBurner feed, and your subscriber count should (as a result) be more accurate.&lt;br /&gt;&lt;br /&gt;To enable this functionality, visit your blog's Settings | Site Feed page, and enter your feed's address in the "Post Feed Redirect URL" field.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;br /&gt;&lt;br /&gt;Note: this feature isn't limited to FeedBurner alone - you can use any feed-mashing service (like &lt;a href="http://pipes.yahoo.com/"&gt;Pipes&lt;/a&gt;) with it.</content>
  24:     <link rel='alternate' type='text/html' href='http://buzz.blogger.com/2007/07/attention-feedburner-fans.html' title='Attention FeedBurner Fans'/>
  25:     <link rel='replies' type='application/atom+xml' href='http://buzz.blogger.com/feeds/5451556303142640543/comments/default' title='Post Comments'/>
  26:     <link rel='self' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default/5451556303142640543'/>
  27:     <link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2399953/posts/default/5451556303142640543'/>
  28:     <author>
  29:       <name>Eric</name>
  30:     </author>
  31:   </entry>
  32:   <entry>
  33:     <id>tag:blogger.com,1999:blog-2399953.post-3571259167003367682</id>
  34:     <published>2007-07-11T16:09:00.000-07:00</published>
  35:     <updated>2007-07-11T16:11:16.103-07:00</updated>
  36:     <category scheme='http://www.blogger.com/atom/ns#' term='polls'/>
  37:     <title type='text'>Polls: out of draft!</title>
  38:     <content type='html'>As of today, the Polls feature has graduated from &lt;a href="http://draft.blogger.com/"&gt;Blogger in Draft&lt;/a&gt;, and is now an official Page Element on www.blogger.com! Polls lets you add a poll to your blog's sidebar, which your visitors can vote on and see results. This video shows how to create a poll:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;      &lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="BLOG_video-7820941d340776aa" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" width="320" height="280" class="BLOG_video_class"&gt; &lt;param name="movie" value="http://www.blogger.com/img/videoplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DlAAAAHfApvOOOB_WlESfHfM9b03EiaeiLzzqu8Asdy_sgy1HkySYnSQocUMiLyatzQFufrZ5BEMSq6SFLzLxE9xlWFIR42FdJ0TbC44ddOlYxgGtvSWaPJ-QcVoTxPMq-WptFxIRzRBqPl3BeR0BgaDoBlKoDDBeTYlhMp8czSi9GUSwBqq7ckN6qgB6tp5_bModxl-qGdG_oyhjiC3Dq3V1jus%26sigh%3DTqojcX0ZT1RvLMLa-d6kJLuNYgI%26begin%3D0%26len%3D86400000%26docid%3D0&amp;amp;nogvlm=1&amp;amp;thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer2%3Fapp%3Dblogger%26contentid%3D7820941d340776aa%26offsetms%3D5000%26itag%3Dw320%26sigh%3DcWUVDFJF9e4lM0KMC_mJHx9gDio&amp;amp;messagesUrl=video.google.com%2FFlashUiStrings.xlb%3Fframe%3Dflashstrings%26hl%3Den"&gt; &lt;param name="bgcolor" value="#FFFFFF"&gt; &lt;embed width="320" src="http://www.blogger.com/img/videoplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DlAAAAHfApvOOOB_WlESfHfM9b03EiaeiLzzqu8Asdy_sgy1HkySYnSQocUMiLyatzQFufrZ5BEMSq6SFLzLxE9xlWFIR42FdJ0TbC44ddOlYxgGtvSWaPJ-QcVoTxPMq-WptFxIRzRBqPl3BeR0BgaDoBlKoDDBeTYlhMp8czSi9GUSwBqq7ckN6qgB6tp5_bModxl-qGdG_oyhjiC3Dq3V1jus%26sigh%3DTqojcX0ZT1RvLMLa-d6kJLuNYgI%26begin%3D0%26len%3D86400000%26docid%3D0&amp;amp;nogvlm=1&amp;amp;thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer2%3Fapp%3Dblogger%26contentid%3D7820941d340776aa%26offsetms%3D5000%26itag%3Dw320%26sigh%3DcWUVDFJF9e4lM0KMC_mJHx9gDio&amp;amp;messagesUrl=video.google.com%2FFlashUiStrings.xlb%3Fframe%3Dflashstrings%26hl%3Den" type="application/x-shockwave-flash" height="280"&gt;&lt;/embed&gt; &lt;/object&gt; &lt;/div&gt;&lt;br /&gt;For more information about the Polls feature, see &lt;a href="http://bloggerindraft.blogspot.com/search/label/polls"&gt;these posts on the Draft blog&lt;/a&gt;.</content>
  39:     <link rel='alternate' type='text/html' href='http://buzz.blogger.com/2007/07/polls-out-of-draft.html' title='Polls: out of draft!'/>
  40:     <link rel='replies' type='application/atom+xml' href='http://buzz.blogger.com/feeds/3571259167003367682/comments/default' title='Post Comments'/>
  41:     <link rel='self' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default/3571259167003367682'/>
  42:     <link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2399953/posts/default/3571259167003367682'/>
  43:     <author>
  44:       <name>Eric</name>
  45:     </author>
  46:   </entry>
  47:   <entry>
  48:     <id>tag:blogger.com,1999:blog-2399953.post-6424897017221638244</id>
  49:     <published>2007-07-09T15:59:00.000-07:00</published>
  50:     <updated>2007-07-09T15:59:00.558-07:00</updated>
  51:     <category scheme='http://www.blogger.com/atom/ns#' term='twitter'/>
  52:     <title type='text'>Twitter badge for Blogger</title>
  53:     <content type='html'>Our friends at &lt;a href="http://www.twitter.com/"&gt;Twitter&lt;/a&gt; just released a nifty new &lt;a href="http://twitter.com/badges/blogger"&gt;web badge&lt;/a&gt;, which you can use to easily display your Twitter updates in your blog's sidebar:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://twitter.com/badges/blogger"&gt;http://twitter.com/badges/blogger&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;(if you're unfamiliar with Twitter, its &lt;a href="http://help.twitter.com/index.php?pg=kb.page&amp;id=26"&gt;FAQ&lt;/a&gt; states that, "Twitter is a community of friends and strangers from around the world sending updates about moments in their lives.  Friends near or far can use Twitter to remain somewhat close while far away.  Curious people can make friends.  Bloggers can use it as a mini-blogging tool. Developers can use the &lt;a href="http://twitter.com/help/api"&gt;API&lt;/a&gt; to make Twitter tools of their own. Possibilities are endless!")&lt;/span&gt;</content>
  54:     <link rel='alternate' type='text/html' href='http://buzz.blogger.com/2007/07/twitter-badge-for-blogger.html' title='Twitter badge for Blogger'/>
  55:     <link rel='replies' type='application/atom+xml' href='http://buzz.blogger.com/feeds/6424897017221638244/comments/default' title='Post Comments'/>
  56:     <link rel='self' type='application/atom+xml' href='http://buzz.blogger.com/feeds/posts/default/6424897017221638244'/>
  57:     <link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2399953/posts/default/6424897017221638244'/>
  58:     <author>
  59:       <name>Eric</name>
  60:     </author>
  61:   </entry>
  62: </feed>

 

I get this xml

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <FeedHierarchy xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
   3:   <year name="2007">
   4:     <month name="07">
   5:       <day name="09">
   6:         <entry>
   7:           <a href="http://buzz.blogger.com/feeds/posts/default/6424897017221638244">Twitter badge for Blogger</a>
   8:         </entry>
   9:       </day>
  10:       <day name="11">
  11:         <entry>
  12:           <a href="http://buzz.blogger.com/feeds/posts/default/5451556303142640543">Attention FeedBurner Fans</a>
  13:         </entry>
  14:         <entry>
  15:           <a href="http://buzz.blogger.com/feeds/posts/default/3571259167003367682">Polls: out of draft!</a>
  16:         </entry>
  17:       </day>
  18:     </month>
  19:   </year>
  20: </FeedHierarchy>

Next steps (sons permitting ;-) )

Publish a simple c# winform that I used to test this xslt

Use this xslt to databind ATOM's feed to a tree view to show the feed history (and maybe construct a custom asp.net control out of it)

«luglio»
domlunmarmergiovensab
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234