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:
- Create XmlReader/XmlWriter of the feed you will be reading/writing.
- Create Atom10FeedFormatter/Rss20FeedFormatter which knows how to read/write the given format (there are Atom10ItemFormatter and Rss20ItemFormatter too)
- Read or write a SyndicationFeed or a SyndicationFeedItem.
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 oneSync 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.
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

