Skip to content

Publishing RSS and Atom Feeds in .Net

Last week we focused on reading RSS and Atom feeds, while this post is all about publishing our own feed. Before you can jump into writing code, you should read up on the different versions of RSS and Atom – especially when you want to use the plain XML approach.

Plain XML to create the feed

As for reading, we can also use plain XML to create our feed. We need to decide what format we want and then regularly check the validator to see if there are problems with what we generate. Then once more, we are on our own and need to specify everything on our own.

We create a MemoryStream, use an XMLWriter and define the metadata of our feed. When this is done, we start the repeating part for each item we want to include in our feed. At the end we flush our writer and return the memory stream:

var output = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(output, new XmlWriterSettings { Indent = true }))
{
    writer.WriteStartDocument();
    writer.WriteStartElement("rss");
    writer.WriteAttributeString("xmlns", "a10", null, "http://www.w3.org/2005/Atom");
    writer.WriteAttributeString("version", "2.0");

    writer.WriteStartElement("channel");

    // Feed metadata
    writer.WriteElementString("title", "My Blog Feed");
    writer.WriteElementString("link", "http://SomeURI");
    writer.WriteElementString("description", "This is a test feed");
    writer.WriteElementString("language", "en-us");

    // Sample RSS item
    writer.WriteStartElement("item");

    writer.WriteStartElement("guid");
    writer.WriteAttributeString("isPermaLink", "false");
    writer.WriteString("ItemThreeID");
    writer.WriteEndElement(); // </guid>

    writer.WriteElementString("title", "#3 - With a Category");
    writer.WriteElementString("author", "C.C@....");
    writer.WriteElementString("link", "http://localhost/Content/three");
    writer.WriteElementString("description", "This is the content for item three");
    writer.WriteElementString("pubDate", DateTimeOffset.UtcNow.AddHours(-1).ToString("r"));
    writer.WriteElementString("updated","a10", DateTimeOffset.UtcNow.ToString("r"));
    writer.WriteElementString("category", ".Net");
    writer.WriteElementString("category", "Practice");
    writer.WriteElementString("category", "Work");

    writer.WriteEndElement(); // </item>

    writer.WriteEndElement(); // </channel>
    writer.WriteEndElement(); // </rss>

    writer.WriteEndDocument();

    writer.Flush();
}

return output;

In our Web API we can create an endpoint that calls this method and sends the result out to the client:

1
2
3
4
5
6
app.MapGet("/feedXml.rss", () =>
{
    var mimeType = "application/rss+xml; charset=utf-8";
    var bytes = FeedCreatorXml.CreateFeed().ToArray();
    return Results.File(bytes, contentType: mimeType);
});

If we open our endpoint in a web browser, we should see an output like this one:

<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>My Blog Feed</title>
    <link>http://SomeURI</link>
    <description>This is a test feed</description>
    <language>en-us</language>
    <item>
      <guid isPermaLink="false">ItemThreeID</guid>
      <title>#3 - With a Category</title>
      <author>C.C@....</author>
      <link>http://localhost/Content/three</link>
      <description>This is the content for item three</description>
      <pubDate>Sat, 3  May 2025 15:01:46 +0100</pubDate>
      <updated xmlns="a10">Sat, 3  May 2025 16:01:46 +0100</updated>
      <category>.Net</category>
      <category>Practice</category>
      <category>Work</category>
    </item>
  </channel>
</rss>

Leveraging SyndicationFeed

A much simpler approach is to use SyndicationFeed to create our feed. Should you not already have installed the package, then you can run this command:

dotnet add package System.ServiceModel.Syndication

We can create our feed and specify the metadata in the constructor of the SyndicationFeed class and add more details through the properties it offers. For each post we want to include in our feed, we create a SyndicationItem and add all the relevant data. We then add all the items to a list and hand it to our feed. The final part is to define the feed format by using the appropriate formatter, write it to a MemoryStream and return the result:

// Example from https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-basic-rss-feed

SyndicationFeed feed = new SyndicationFeed("My Blog Feed", "This is a test feed", new Uri("http://SomeURI"));
feed.Authors.Add(new SyndicationPerson("[email protected]", "Johnny Graber", "https://improveandrepeat.com/"));
feed.Categories.Add(new SyndicationCategory(".Net"));
feed.Description = new TextSyndicationContent("Basic example to produce a RSS feed");
feed.ImageUrl = new Uri("https://jgraber.ch/images/background.jpg");
feed.LastUpdatedTime = DateTimeOffset.Now;


SyndicationItem item3 = new SyndicationItem(
    "#3 - With a Category",
    "This is the content for item three",
    new Uri("http://localhost/Content/three"),
    "ItemThreeID",
    DateTimeOffset.Now);
item3.PublishDate = DateTimeOffset.Now.AddHours(-1);
item3.Authors.Add(new SyndicationPerson("C.C@...."));
item3.Categories.Add(new SyndicationCategory(".Net"));
item3.Categories.Add(new SyndicationCategory("Practice"));
item3.Categories.Add(new SyndicationCategory("Work"));

List<SyndicationItem> items = new List<SyndicationItem>();
items.Add(item3);

feed.Items = items;

var rssFormatter = new Rss20FeedFormatter(feed, true);

var output = new MemoryStream();
var settings = new XmlWriterSettings
{
    Indent = true,
    Encoding = Encoding.UTF8
};

using (var writer = XmlWriter.Create(output, settings))
{
    rssFormatter.WriteTo(writer);
    writer.Flush();
    return output;
}

In our Web API we can use a method like this to publish our feed:

1
2
3
4
5
6
app.MapGet("/feedSyn.rss", () =>
{
    var mimeType = "application/rss+xml; charset=utf-8";
    var bytes = FeedCreator.CreateFeed().ToArray();
    return Results.File(bytes, contentType: mimeType);
});

If we open the endpoint in a web browser, we get a similar output to the one from the plain XML approach:

<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>My Blog Feed</title>
    <link>http://someuri/</link>
    <description>Basic example to produce a RSS feed</description>
    <managingEditor>[email protected]</managingEditor>
    <lastBuildDate>Sat, 3  May 2025 15:01:46 +0100</lastBuildDate>
    <category>.Net</category>
    <image>
      <url>https://jgraber.ch/images/background.jpg</url>
      <title>My Blog Feed</title>
      <link>http://someuri/</link>
    </image>
    <item>
      <guid isPermaLink="false">ItemThreeID</guid>
      <link>http://localhost/Content/three</link>
      <author>C.C@....</author>
      <category>.Net</category>
      <category>Practice</category>
      <category>Work</category>
      <title>#3 - With a Category</title>
      <description>This is the content for item three</description>
      <pubDate>Sat, 3  May 2025 15:01:46 +0100</pubDate>
      <a10:updated>2025-05-03T16:01:46+01:00</a10:updated>
    </item>
  </channel>
</rss>

Conclusion

While the plain XML approach let us specify the feed exactly as we want it to be, but we must do an awful amount of extra work. It can be done, but I rather spend my time on other things than tracking down XML validation problems.

The SyndicationFeed package does all the heavy lifting for us and we can focus on creating the content for the feed. While we cannot specify every detail, we need a lot less knowledge to create a valid feed.

If I can choose the tool for my projects, I go with the SyndicationFeed package. That way I get a usable abstraction to read feeds and have everything in place should I need to publish one.

I hope this little overview helps you with the creation of your RSS and Atom feeds.