Trivial RSS Writer
Recently, I've been going through the standard fatherhood ritual of trying to setup of a webpage to share out photos of my kid. This naturally involves creating an RSS feed so that family can subscribe to the new image list.
I wanted to be able to write simple C# code like this to produce the feed:
static void WriteWholeFile()
{
XmlWriter xml = new XmlTextWriter(new StreamWriter("myfeed.xml"));
RssWriter rss = new RssWriter(xml);
rss.WriteHeader("My Mini blog", "https://xyz.com", "My site of baby pictures", null);
rss.WriteItem(
"First item",
"Hi! Here's the <b>first</b> item",
new System.Uri("https://xyz.com/1.html"),
new DateTime(1999, 12, 1));
rss.WriteItem(
"Second item",
"Here's the <b>2nd</b> item.",
new System.Uri("https://xyz.com/2.html"),
new DateTime(1988, 10, 4));
rss.Close();
xml.Close();
}
Here's some trivia about some of the decisions I made here:
- I'm no RSS/XML/Web expert, so I have no doubt there are many better implementations out there.
- Copying one of these other writers would violate the unspoken rules of this standard fatherhood ritual. (as would using Flickr)
- My main goal was simplicity, as illustrated by the usage snippet above.
- It's producing valid RSS 2.0 feeds. You can validate the feeds by URL at https://feedvalidator.org, or by pasting in the text at https://validator.w3.org/feed. And they also work in SharpReader (which is what my target audience is using).
- DateTimeFormatInfo is a live-saver to get the the DateTime into the date format needed for an RSS feed.
- Since RSS is just XML, I choose to build RSSWriter on an XmlWriter. This lets the caller have great flexibility about where the XML goes (a local file, a string, a node in an XML Dom)
- The RSSWriter does not own the underlying XmlWriter, and so RSSWriter.Close() does not close the underlying streams.
- I avoided XML Serialization (the method Dare used in RSSBandit) because I wanted a simple, forward writing, stateless, writer. This is similar to using XmlWriter over the XML Dom.
Here's the code: [Updated: made some minor tweaks, including rename RSS-->Rss per fxcop guidelines]
using System;
using System.Xml;
using System.IO;
// Class to write out RSS
// Expected Usage:
// rss = new RSSWriter(...);
// rss.WriteHeader(...);
// rss.WriteItem(..);
// rss.WriteItem(..);
// rss.WriteItem(..);
// rss.Close();
// Validate feeds by URL: https://feedvalidator.org, or https://validator.w3.org/feed
// Code for RSS writer from https://blogs.msdn.com/jmstall
class RssWriter
{
XmlWriter m_writer;
public RssWriter(XmlWriter tw)
{
m_writer = tw;
}
// Write header for RSS Feed
// Parameters:
// title - Pretty title of the RSS feed. Eg "My Pictures"
// link - optional URL to link RSS feed back to a web page.
// description - more verbose human-readable description of this feed.
// generator - optional string for 'generator' tag.
public void WriteHeader(string title, string link, string description, string generator)
{
m_writer.WriteStartElement("rss");
m_writer.WriteAttributeString("version", "2.0");
m_writer.WriteStartElement("channel");
m_writer.WriteElementString("title", title);
if (link != null)
{
m_writer.WriteElementString("link", link); // link to generated report.
}
m_writer.WriteElementString("description", description);
if (generator != null)
{
m_writer.WriteElementString("generator", generator);
}
m_writer.WriteElementString("lastBuildDate", ConvertDate(new DateTime()));
}
// Write out an item.
// title - title of the blog entry
// content - main body of the blog entry
// link - link the blog entry back to a webpage.
// time - date for the blog entry.
public void WriteItem(string title, string content, System.Uri link, DateTime time)
{
m_writer.WriteStartElement("item");
WriteItemBody(m_writer, title, content, link, time);
m_writer.WriteEndElement(); // item
}
// Write just the body (InnerXml) of a new item
public static void WriteItemBody(XmlWriter w, string title, string content, System.Uri link, DateTime time)
{
w.WriteElementString("title", title);
if (link != null) { w.WriteElementString("link", link.ToString()); }
w.WriteElementString("description", content); // this will escape
w.WriteElementString("pubDate", ConvertDate(time));
}
// Close out the RSS stream.
// this does not close the underlying XML writer.
public void Close()
{
m_writer.WriteEndElement(); // channel
m_writer.WriteEndElement(); // rss
}
// Convert a DateTime into the format needed for RSS (from RFC 822).
// This looks like: "Wed, 04 Jan 2006 16:03:00 GMT"
static string ConvertDate(DateTime t)
{
// See this for help on format string
// https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcustomdatetimeformatstrings.asp
DateTime t2 = t.ToUniversalTime();
return t2.ToString(@"ddd, dd MMM yyyy HH:mm:ss G\MT");
}
}
Comments
- Anonymous
January 14, 2006
Nice. But according to the framework naming guidelines, shouldn't that class be named RssWriter? - Anonymous
January 15, 2006
I needed a similar thing and I cooked up an ugly xml dumper at http://blogs.msdn.com/abhinaba/archive/2005/12/21/506277.aspx
Too bad your blog was not up by that time :( - Anonymous
January 16, 2006
Kevin - you caught me red-handed. I mdae an update.