He who joyfully marches to music in rank and file has already earned my contempt. He has been given a large brain by mistake, since for him the spinal cord would suffice. --Albert Einstein

Easily Build An Atom or RSS Feed With C# and the Syndication Namespace

Martian Sky. - 18 jan 2011.  Copyright © Steve Lautenschlager

Summary

So you want to create your own syndication feed. There are a lot of questions:

  • Will I use an existing library or roll my own?
  • If I use an existing library is it reliable and easy to use?
  • Will it cost me?
  • Which protocol will I use, RSS or Atom?

I had these same questions and ultimately landed on the Syndication namespace in Windows Communication Foundation.

The Code

using System.ServiceModel.Syndication;

public static void UpdateFeed(HttpRequest req)
{
   // This code sample by Steve Lautenschlager at CambiaResearch.com

   string baseUrl = req.Url.Scheme + "://" + req.Url.Authority;

   #region -- Load database records --
   // List<Article> articles = // TODO: add your logic to retrieve blog entries
   #endregion

   #region -- Create Feed --
   SyndicationFeed feed = new SyndicationFeed();

   // set the feed ID to the main URL of your Website
   feed.Id = baseUrl;

   // set some properties on the feed
   feed.Title = new TextSyndicationContent("Cambia Research");
   feed.Description = new TextSyndicationContent(".Net Articles and Code Samples Since 2002");
   feed.Copyright = new TextSyndicationContent("Steve");
   feed.LastUpdatedTime = new DateTimeOffset(DateTime.Now);
  
   // since you are writing your own custom feed generator, you get to
   // name it!  Although this is not required.
   feed.Generator = "Cambia Research Website Framework 5.0";

   // add your site or feed logo url
   string imageUrl = baseUrl + "/images/mylogo.gif";
   feed.ImageUrl = new Uri(imageUrl);

   // Add the URL that will link to your published feed when it's done
   SyndicationLink link = new SyndicationLink(new Uri(baseUrl + "/myfeed.xml"));
   link.RelationshipType = "self";
   link.MediaType = "text/html";
   link.Title = "Cambia Research Feed";
   feed.Links.Add(link);

   // Add your site link
   link = new SyndicationLink(new Uri(baseUrl));
   link.MediaType = "text/html";
   link.Title = "Cambia Research";
   feed.Links.Add(link);
   #endregion

   #region -- Loop over your blog entries to add feed items --
   List<SyndicationItem> items = new List<SyndicationItem>();

   int maxItems = 15;
   for (int i=0; i < articles.Count; i++)
   {
      Article a = articles[i];

      // create new entry for feed
      SyndicationItem item = new SyndicationItem();

      // set the entry id to the URL for the item
      string url = GetArticleUrl(a);  // TODO: create the GetArticleUrl method
      url = url;
      item.Id = url;

      // Add the URL for the item as a link
      link = new SyndicationLink(new Uri(url));
      item.Links.Add(link);

      // Fill some properties for the item
      item.Title = new TextSyndicationContent(a.Title);
      item.LastUpdatedTime = a.Updated;
      item.PublishDate = a.Created;

      // Fill the item content            
      string html = GetFeedEntryHtml(a); // TODO: create the GetFeedEntryHtml method
      TextSyndicationContent content 
           = new TextSyndicationContent(html, TextSyndicationContentKind.Html);
      item.Content = content;

      // Finally add this item to the item collection
      items.Add(item);

      // Stop after adding the max desired number of items
      if (items.Count >= maxArticles)
      {
         break;
      }

   }

   feed.Items = items;

   #endregion
   
   #region -- Output feed to a file --
   string mainFile = HttpContext.Current.Server.MapPath("~/feed.xml");
   using (FileStream fs = new FileStream(mainFile, FileMode.Create))
   {
      using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
      {
         XmlWriterSettings xs = new XmlWriterSettings();
         xs.Indent = true;
         using (XmlWriter xw = XmlWriter.Create(w, xs))
         {
            xw.WriteStartDocument();
            Atom10FeedFormatter formatter = new Atom10FeedFormatter(feed);
            //Rss20FeedFormatter formatter = new Rss20FeedFormatter(feed);
            formatter.WriteTo(xw);
            xw.Close();
         }
      }
   }
   #endregion

   

   
   


}

Key Points of the Code

First notice the using statement at the top:

using System.ServiceModel.Syndication;

You may find that you need to add a reference to the System.ServiceModel namespace. Otherwise, the Syndication namespace won't be available. [SideNote: You'll probably need the other standard namespaces, too, but I left them out for simplicity.]

The first thing we do in the code is create a new SyndicationFeed object and set properties on the feed itself. Then we add individual entries to that feed in our for loop.

Inside the for loop, notice where we create the content for the entry:

// Fill the item content            
string html = GetFeedEntryHtml(a); // TODO: create the GetFeedEntryHtml method
TextSyndicationContent content = new TextSyndicationContent(html, TextSyndicationContentKind.Html);
item.Content = content;

Notice especially the second argument of the TextSyndicationContent constructor. This allows us to specify the type of text content. So far, there are only three choices: Html, Plaintext and XHtml. If you don't specify this second argument the default is Plaintext. If you specify plain text, but you actually add HTML as I did, the feed readers will generally render your HTML as pure, basic text. Which was not what I want and, I'm guessing, not what you want either. Most people will likely want to ad HTML content to their feed items to make it a bit more visually appealing. This tripped me up for a bit, so I just wanted to make a point of this.

Finally, jump down to the part where we write the feed to a file:

Atom10FeedFormatter formatter = new Atom10FeedFormatter(feed);
//Rss20FeedFormatter formatter = new Rss20FeedFormatter(feed);
formatter.WriteTo(xw);

Notice that the Syndication namespace provides us with at least two options for exporting the feed we just created: RSS or Atom. Aside from the simplicity, this is one of the big reasons I chose to write my RSS feed using .NET. I didn't know which type of feed would be best to create. This way, I didn't have to decide. I could easily change later if I needed to.

One final thing: after I completed this article I discovered that my feed was written with a utf-16 encoding. Some browsers fail to support that encoding. The reason it was utf-16 was because I used a StringBuilder for the underlying storage for XmlWriter. Apparently, the StringBuilder always defaults to utf-16. I have corrected this by connecting directly to my output stream and specifying the encoding to be utf-8. The code above and the XML below reflect this change. Just an FYI in case you're thinking StringBuilder would suit your purposes better.

The XML

I chose to export my feed in the Atom format. This is what it looks like.

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Cambia Research</title>
  <subtitle type="text">.NET Articles and Code Samples Since 2002</subtitle>
  <id>http://www.cambiaresearch.com</id>
  <rights type="text">Cambia Research</rights>
  <updated>2012-01-14T17:14:34-06:00</updated>
  <logo>http://www.cambiaresearch.com/r/i/cr50_20111025.png</logo>
  <generator>Cambia Research Website Framework 5.0</generator>
  <link rel="self" type="text/html" title="Cambia Research Feed" href="http://www.cambiaresearch.com/feed.xml" />
  <link type="text/html" title="Cambia Research" href="http://www.cambiaresearch.com/" />
  <entry>
    <id>http://www.cambiaresearch.com/articles/70/page-goes-gold-with-one-million-views</id>
    <title type="text">Page Goes Gold With One Million Views</title>
    <published>2011-12-12T17:44:15-06:00</published>
    <updated>2011-12-29T22:57:10-06:00</updated>
    <link href="http://www.cambiaresearch.com/articles/70/page-goes-gold-with-one-million-views" />
    <content type="html">&lt;img src='http://www.cambiaresearch.com/r/ai/e699c4c1-e49e-4cd6-8df6-0b4fd0536986_ArticleImageThumb_0.jpg' /&gt;</content>
  </entry>

          ... several more items ...

  <entry>
    <id>http://www.cambiaresearch.com/articles/32/change-the-default-browser-in-visual-studio-2005-and-visual-web-developer</id>
    <title type="text">Change the Default Browser in Visual Studio 2005 and Visual Web Developer</title>
    <published>2007-02-19T21:20:10-06:00</published>
    <updated>2012-01-07T23:17:55-06:00</updated>
    <link href="http://www.cambiaresearch.com/articles/32/change-the-default-browser-in-visual-studio-2005-and-visual-web-developer" />
    <content type="html">&lt;img src='http://www.cambiaresearch.com/r/ai/83b1ee3e-e1b9-40ef-9505-7276007635d4_ArticleImageThumb_0.jpg' /&gt;&lt;p&gt;How to change the default browser in Visual Studio.  That's right, you aren't stuck with just Internet Explorer.&lt;/p&gt;</content>
  </entry>
</feed>

How Many Items Should I Include In My Feed?

First and foremost, don't add all of your articles or blog entries! A feed is not intended to be an archive of your entire site. It is only intended to keep up to date with the latest activity on your site.

As for the exact number, somewhere between 10 and 25 is probably sufficient for most every situation.

Up Next - Feedburner.com

In the next article I'll talk about how to integrate your feed with the Feedburner service at Feedburner.com.