Daniel Cazzulino's Blog : Forget about writing Atom or RSS XML handling code ever again

Subscriptions

News

Source code published in this blog is public domain unless otherwise specified.

 

kzu in LinkedIn

  Microsoft MVP Profile

 Contact

Post Categories

Forget about writing Atom or RSS XML handling code ever again

A *very* welcome addition to .NET 3.5, which just went RTM for MSDN subscribers and trial for the rest before general availability early next year: System.ServiceModel.Syndication.

This namespace, which lives in the System.ServiceModel.Web.dll assembly which provides the WCF Syndication functionality, contains useful classes for working with feeds and items. I won't go over the Architecture of Syndication, How the WCF Syndication Object Model Maps to Atom and RSS, How to: Create a Basic RSS Feed, How to: Create a Basic RSS Feed, How to: Expose a Feed as both Atom and RSS or the basics of Syndication Extensibility. All those links provide enough to get you started.

The typical usage is:

 

The only thing I felt was missing was a factory that abstracts me from having to decide which formatter to create for a given source: the factory should be able to determine this automatically depending on the first element of the feed/item.

With the factory in place, reading a feed without caring for its format, is as simple as:

using (XmlReader reader = XmlReader.Create(atomFeedUrl))
{
    SyndicationFeedFormatter formatter = SyndicationFormatterFactory.CreateFeedFormatter(reader);
    formatter.ReadFrom(reader);
    SyndicationFeed feed = formatter.Feed;
}

Note that there's nothing in my code that knows about RSS or Atom.

Processing of the item and extensions is very friendly and quite flexible, making for a very nice platform to base syndication extension processing plugins:

    foreach (SyndicationItem item in feed.Items)
    {
        // ... process item

        foreach (SyndicationElementExtension extension in item.ElementExtensions)
        {
            // ... process extensions
            // example: SSE sync
            if (extension.OuterNamespace == Sync.NamespaceUri)
            {
                // NOTE: we don't need to pass an explicit XmlSerializer because we implement 
                // IXmlSerializable, so the library figures out it needs one :)
                Sync sync = extension.GetObject<Sync>();
                // ... do something with the extension
            }
            else if (extension.OuterName == "Mock")
            {
                // this extension does not implement IXmlSerializable neither is a WCF data 
                // contract, so we need to pass an XmlSerializer
                Mock m = extension.GetObject<Mock>(new XmlSerializer(typeof(Mock)));
                // ... do something with the extension.
            }
        }
    }

So here goes such a factory:

/// <summary>
/// Creates formatters for RSS 2.0 and Atom 1.0 according to the input content.
/// </summary>
/// <remarks> /// <see cref="http://www.clariusconsulting.net/kzu">Created by Daniel Cazzulino.</see> /// </remarks>
public static class SyndicationFormatterFactory { static XmlReaderSettings settings; static SyndicationFormatterFactory() { // Makes the processing faster for the readers we create. settings = new XmlReaderSettings(); settings.IgnoreComments = true; settings.IgnoreProcessingInstructions = true; settings.IgnoreWhitespace = true; settings.CheckCharacters = true; settings.CloseInput = true; } /// <summary> /// Creates a <see cref="SyndicationFeedFormatter"/> according to the /// input format. /// </summary> /// <param name="uriString">Feed location</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 feed.</exception> public static SyndicationFeedFormatter CreateFeedFormatter(string uriString) { using (XmlReader reader = XmlReader.Create(uriString, settings)) return CreateFeedFormatter(reader); } /// <summary> /// Creates a <see cref="SyndicationFeedFormatter"/> according to the /// input format. /// </summary> /// <param name="uri">Feed location</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 feed.</exception> public static SyndicationFeedFormatter CreateFeedFormatter(Uri uri) { using (XmlReader reader = XmlReader.Create(uri.ToString(), settings)) return CreateFeedFormatter(reader); } /// <summary> /// Creates a <see cref="SyndicationFeedFormatter"/> according to the /// input format. /// </summary> /// <param name="reader">Feed source</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 feed.</exception> public static SyndicationFeedFormatter CreateFeedFormatter(XmlReader reader) { if (reader.ReadState == ReadState.Initial) { reader.MoveToContent(); } Rss20FeedFormatter rss = new Rss20FeedFormatter(); if (rss.CanRead(reader)) { return rss; } Atom10FeedFormatter atom = new Atom10FeedFormatter(); if (atom.CanRead(reader)) { return atom; } throw new NotSupportedException("Invalid feed root element: " + reader.Name); } /// <summary> /// Creates a <see cref="SyndicationItemFormatter"/> according to the /// input format. /// </summary> /// <param name="uriString">Item location</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 item.</exception> public static SyndicationItemFormatter CreateItemFormatter(string uriString) { using (XmlReader reader = XmlReader.Create(uriString, settings)) return CreateItemFormatter(reader); } /// <summary> /// Creates a <see cref="SyndicationItemFormatter"/> according to the /// input format. /// </summary> /// <param name="uri">Item location</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 item.</exception> public static SyndicationItemFormatter CreateItemFormatter(Uri uri) { using (XmlReader reader = XmlReader.Create(uri.ToString(), settings)) return CreateItemFormatter(reader); } /// <summary> /// Creates a <see cref="SyndicationItemFormatter"/> according to the /// input format. /// </summary> /// <param name="reader">Item source</param> /// <exception cref="NotSupportedException">The input does not contain a valid RSS 2.0 or Atom 1.0 item.</exception> public static SyndicationItemFormatter CreateItemFormatter(XmlReader reader) { if (reader.ReadState == ReadState.Initial) { reader.MoveToContent(); } Rss20ItemFormatter rss = new Rss20ItemFormatter(); if (rss.CanRead(reader)) { return rss; } Atom10ItemFormatter atom = new Atom10ItemFormatter(); if (atom.CanRead(reader)) { return atom; } throw new NotSupportedException("Invalid item element: " + reader.Name); } }

 

You can also download the plain text representation for easier copying.

 

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

posted on Thursday, November 22, 2007 9:02 AM by kzu