Skip to content

Reading RSS and Atom Feeds in .Net

Since there is no more a single social network where we can find all the interesting things that happen in software development, it may be time to go back to the old technologies and revive RSS and Atom feeds. That way at least we can collect the many different blogs into a single source and build up on that.

In this post we explore 3 different ways to read RSS and Atom feeds in C#, while we will explore our options to publish a feed in the next post.

Preparation

To get a reliable way to read RSS and Atom feeds for my tests, I created a minimalistic Web API project and served this test file in the /feed.rss endpoint:

<?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 16: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>

To encode the HTML in the feed we use the HtmlAgilityPack through this method in all the examples:

private string ReadAsText(string input)
{
    if (String.IsNullOrWhiteSpace(input))
    {
        return string.Empty;
    }
    var htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(input);
    return HttpUtility.HtmlDecode(htmlDoc.DocumentNode.InnerText);
}

FeedReader

The FeedReader by Armin Reiter is a user-friendly library to read RSS and Atom feeds in C#. Unfortunately, the development seams not that active, but so far it works, and RSS is not going to change much. But keep that in mind before you start a new project with it. We can install it with this command:

dotnet add package CodeHollow.FeedReader

We can read the URL to an RSS feed with the ReadAsync() method. That gives us an instance that encapsulates the whole feed and offers us access to the interesting parts through clearly named methods. The posts are in the Items collection that we can iterate through to get to the different posts:

var blogFeed = "https://localhost:7066/feed.rss";

var feed = await FeedReader.ReadAsync(blogFeed);

Console.WriteLine("Feed Title: " + feed.Title);
Console.WriteLine("Feed Description: " + feed.Description);
Console.WriteLine("Feed Image: " + feed.ImageUrl);
Console.WriteLine("Url: " + feed.Link);
Console.WriteLine("Last updated: " + feed.LastUpdatedDate);

foreach (var item in feed.Items)
{
    Console.WriteLine("-------");
    Console.WriteLine(item.Link);
    Console.WriteLine(item.Title);
    Console.WriteLine(item.Author);
    Console.WriteLine(item.PublishingDate);
    Console.WriteLine("no update date");
    Console.WriteLine(string.Join(" - ", item.Categories));
    Console.WriteLine(ReadAsText(item.Description));
}

If we run this code, we get this output for our demo feed:

Feed Title: My Blog Feed
Feed Description: Basic example to produce a RSS feed
Feed Image: https://jgraber.ch/images/background.jpg
Url: http://someuri/
Last updated: 03.05.2025 15:01:46
-------
http://localhost/Content/three
#3 - With a Category
C.C@....
03.05.2025 15:01:46
no update date
.Net - Practice - Work
This is the content for item three

FeedReader does not offer a field to get to the updated date, something that we can access with the other libraries.

I find this library a great help but be aware that it only can read feeds and does not offer a feature to publish them.

Plain XML

RSS is based on XML; therefore, we can always fall back to an XML based solution to read a feed. Since the different versions of RSS and Atom are a pain, we have to do a lot more work to read data – especially if we must support multiple versions of the protocols.

We start with an XmlDocument and load our feed into it. Then we need to define the Atom namespace and read the node with the channel information. We can access the different fields through SelectSingleNode and InnerText.

The posts are inside the item nodes, where we repeat the low-level access:

// Based on examples from: https://www.dotnetspider.com/resources/43786-How-to-parse-RSS-feeds-in-ASPNET.aspx

XmlDocument rssXmlDoc = new XmlDocument();
rssXmlDoc.Load(blogFeed);

var nsmgr = new XmlNamespaceManager(rssXmlDoc.NameTable);
nsmgr.AddNamespace("a10", "http://www.w3.org/2005/Atom");

var feedNode = rssXmlDoc.SelectSingleNode("rss/channel");

var feedTitle = feedNode?.SelectSingleNode("title")?.InnerText;
var feedDescription = feedNode?.SelectSingleNode("description")?.InnerText;
var feedImageUrl = feedNode?.SelectSingleNode("image/url")?.InnerText;
var feedUrl = feedNode?.SelectSingleNode("link")?.InnerText;
var feedUpdate = feedNode?.SelectSingleNode("lastBuildDate")?.InnerText;

Console.WriteLine("Feed Title: " + feedTitle);
Console.WriteLine("Feed Description: " + feedDescription);
Console.WriteLine("Feed Image: " + feedImageUrl);
Console.WriteLine("Url: " + feedUrl);
Console.WriteLine("Last updated: " + feedUpdate);


XmlNodeList rssNodes = rssXmlDoc.SelectNodes("rss/channel/item");
foreach (XmlNode rssNode in rssNodes)
{
    var link = rssNode.SelectSingleNode("link")?.InnerText;
    var title = rssNode.SelectSingleNode("title")?.InnerText;
    var author = rssNode.SelectSingleNode("author")?.InnerText;
    var publishDate = rssNode.SelectSingleNode("pubDate")?.InnerText;
    var updateDate = rssNode.SelectSingleNode("a10:updated", nsmgr)?.InnerText;
    var categories = new List<string>();
    foreach (XmlElement item in rssNode.SelectNodes("category"))
    {
        categories.Add(item.InnerText);
    }
    var description = rssNode.SelectSingleNode("description")?.InnerText;

    Console.WriteLine("-------");
    Console.WriteLine(link);
    Console.WriteLine(title);
    Console.WriteLine(author);
    Console.WriteLine(publishDate);
    Console.WriteLine(updateDate);
    Console.WriteLine(string.Join(" - ", categories));
    Console.WriteLine(ReadAsText(description));
}

If we run this code, we get this output:

Feed Title: My Blog Feed
Feed Description: Basic example to produce a RSS feed
Feed Image: https://jgraber.ch/images/background.jpg
Url: http://someuri/
Last updated: Sat, 3  May 2025 16:01:46 +0100
-------
http://localhost/Content/three
#3 - With a Category
C.C@....
Sat, 3  May 2025 15:01:46 +0100
2025-05-03T16:01:46+01:00
.Net - Practice - Work
This is the content for item three

As we can see for the dates, we need to convert the string on our own. But it allows us to access the updated date that we could not find with FeedReader.

The benefit of using XML directly is that we can access everything that is part of the feed. That flexibility comes with the price that we need to do the whole work and cannot build on top of a solution.

SyndicationFeed

The SyndicationFeed is part of .Net and offers us an abstraction on top of the XML that understands feeds. We can install the SyndicationFeed package with this command:

dotnet add package System.ServiceModel.Syndication

We start with an XmlReader reader that then pass to the SyndicationFeed to read our feed object. On this object we can access the fields and do not need to work directly with XML:

XmlReader reader = XmlReader.Create(blogFeed);
SyndicationFeed feed = SyndicationFeed.Load(reader);
reader.Close();


Console.WriteLine("Feed Title: " + feed.Title?.Text);
Console.WriteLine("Feed Description: " + feed.Description?.Text);
Console.WriteLine("Feed Image: " + feed.ImageUrl);
Console.WriteLine("Url: " + feed.Links?.FirstOrDefault()?.Uri);
Console.WriteLine("Last updated: " + feed.LastUpdatedTime);


foreach (var item in feed.Items)
{
    Console.WriteLine("-------");
    Console.WriteLine(item.Links?.FirstOrDefault()?.Uri);
    Console.WriteLine(item.Title?.Text);
    Console.WriteLine(item.Authors?.FirstOrDefault()?.Email);
    Console.WriteLine(item.PublishDate); 
    Console.WriteLine(item.LastUpdatedTime);

    var categories = new List<string>();
    foreach (var category in item.Categories)
    {
        categories.Add(category.Name);
    }

    Console.WriteLine(string.Join(" - ", categories));
    Console.WriteLine(ReadAsText(item.Summary?.Text));
}

If we run this code, we get this output:

Feed Title: My Blog Feed
Feed Description: Basic example to produce a RSS feed
Feed Image: https://jgraber.ch/images/background.jpg
Url: http://someuri/
Last updated: 03.05.2025 16:01:46 +01:00
-------
http://localhost/Content/three
#3 - With a Category
C.C@....
03.05.2025 15:01:46 +01:00
03.05.2025 16:01:46 +01:00
.Net - Practice - Work
This is the content for item three

Here we get all date values in the same format without any additional effort at our end.

The SyndicationFeed has the benefit of offering a higher level of abstraction over XML and is maintained as part of .Net.

Next

The tree approaches in this post summarize the options we have. Either we work with XML directly, use an abstraction on top of XML or go for a library that hides the whole XML format from us. If I only would need to read RSS and Atom feeds, I would go for FeedReader. But let us not jump to a conclusion before we covered the publishing side that is the topic of the next post.