Skip to main content

A rich and extremely tolerant .NET RSS and ATOM feed parser and writer. Parses the wildest real-world feeds without ever throwing, unifies RSS and Atom into one model, and writes clean canonical RSS 2.0 / Atom 1.0 back out.

Total Downloads
15.1K
15,078
Published
Aug 21, 2026
2mo ago

Get Started

dotnet add package SimpleFeed --version 4.0.0
ProductVersions
.NET
net10.0

README

SimpleFeed

A rich and extremely tolerant .NET RSS and Atom feed parser — and, as of v4, writer. In production parsing wild real-world feeds hourly since ~2018.

The central objectives, unchanged since the beginning:

  1. Get as much information as possible from a feed — including the standard extensions (iTunes podcast tags, media RSS, Dublin Core, content:encoded, rawvoice), and Atom-namespaced elements appearing inside RSS feeds.

  2. Be extremely fault tolerant. Real-world feeds are wild: obsolete EST-style timezones, missing dates, malformed authors, bare www. links, spaces inside URLs, mis-declared encodings, garbage bytes. Parsing never throws — failures land as Error + Messages on the result, invalid pieces are skipped, absent dates report honestly as DateTimeOffset.MinValue.

  3. One set of types for both formats. A single model (SFFeed / SFFeedEntry) represents what was originally RSS or Atom — without losing the format-specific information (typed Atom texts, category schemes, rel values, the original declared mime-type string, …).

  4. v4 makes good on the old promise: serialization. The same model writes back out as clean, canonical RSS 2.0 or Atom 1.0 — so SimpleFeed can now unify any wild feed into one well-formed output.

Install

dotnet add package SimpleFeed

v4 targets .NET 10. (v3.x, targeting netstandard2.0/net6.0, lives on the master branch for legacy patch releases.)

Reading a feed

using SimpleFeed;

byte[] bytes = await httpClient.GetByteArrayAsync(feedUrl); // fetching is yours; SimpleFeed parses
SFFeed feed = SFFeed.Parse(bytes);

if(feed.Error) {
	Console.WriteLine(feed.Messages); // never throws — failures report here
	return;
}

Console.WriteLine($"{feed.Title} — {feed.Items.Count} entries ({feed.SourceFeedType})");

foreach(SFFeedEntry e in feed.Items) {
	Console.WriteLine($"{e.Published:d}  {e.Title}  by {e.Author}");

	SFLink web = e.GetFirstWebLink_AlternateWins(); // the tournament: picks "the" web link among many
	SFLink enclosure = e.GetFirstEnclosure();       // podcast audio, PDFs, images…

	if(enclosure != null)
		Console.WriteLine($"    enclosure: {enclosure.Url} ({enclosure.Type}, {enclosure.Length} bytes)");
}

Parse accepts byte[] (preferred — honors BOMs and the XML prolog's declared encoding) or string. It always returns a non-null SFFeed; check Error.

Settings

SFFeed feed = SFFeed.Parse(bytes, new SFFeedSettings() {
	AlterUTCDatesToThisTimezone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"),
	NumberOfEntriesToParseLimit = 25,
	KeepXmlDocument = true,             // retain the source XElements (off by default)
	ConvertContentUrlsToLinks = true,   // harvest bare urls out of body text into Links (marked DiscoveredLink)
	GenerateIdForEntriesWithNoId = (entry, idBase) => idBase + Guid.NewGuid(),
	AlterEntryOnComplete = entry => entry.Title == null ? null : entry, // returning null drops the entry
});

Writing a feed (new in v4)

SFFeed feed = SFFeed.Parse(bytes);   // any wild feed in…
string rss = feed.ToRssXml();        // …clean RSS 2.0 out
string atom = feed.ToAtomXml();      // …or clean Atom 1.0

Or build one from scratch:

SFFeed feed = new() {
	Title = "My Feed",
	Language = "en-us",
	Items = [
		new SFFeedEntry() {
			Id = "https://example.com/post/1",
			Title = "Hello",
			Published = DateTimeOffset.UtcNow,
			Content = "The body.",
		},
	],
};

string rss = feed.ToRssXml();

The writer carries the same spirit as the reader: it never throws over imperfect model state. Absent dates are omitted (never emitted as year 0001), XML-illegal characters are stripped, links the parser merely discovered in body text are excluded (opt-in via SFWriteSettings.IncludeDiscoveredLinks), library-generated guid prefixes are restored to their source form, inherited entry authors aren't duplicated onto every item, and the synthetic detection-only mime types (video/vimeo, video/youtube) are never emitted. Parse → write → reparse is a stable cycle, covered by the test suite.

Extension coverage

Extension Read
Atom elements inside RSS (atom:link, atom:published, atom:updated, atom:category) ✔
iTunes podcast (author, subtitle, summary, keywords, image, duration, explicit, episode, season) ✔
content:encoded (with the description-vs-content reconciliation RSS never specified) ✔
Dublin Core (dc:creator, dc:date) ✔
Media RSS (media:content, media:thumbnail) ✔
rawvoice (metamark) ✔
PubSubHubbub (rel="hub" / rel="self" → SFFeed.Hub) ✔

RSS 1.0/RDF is not supported (it errors cleanly).

v4 breaking changes (from v3.x)

v4 is a renewal release: .NET 10, a model/parser split, the write side, and zero dependencies (the last one, DotNetXtensions.Mini, is vendored internal as of 4.0 — only the micro string/LINQ helpers SimpleFeed actually uses, copied verbatim). The headlines:

  • Namespace is now SimpleFeed (was SimpleFeedNS).
  • SFFeed replaces the old SimpleFeed type: parse via static SFFeed.Parse(bytes) — no more constructing a feed and calling instance Parse. The result is always non-null; entries are on feed.Items (the old feed-is-an-IList face is gone).
  • The parse machinery that used to be public (RssItemToFeedEntry, AtomEntryToFeedEntry, SetItunes, …) is internal now; the public surface is the model + Parse + the write methods + the hook points.
  • BasicMimeType and friends moved into the SimpleFeed namespace (the DotNetXtensions.MimeTypes dependency was internalized).
  • Youtube/vimeo detection lives only in the SFAlterFeedLinks hook now (SFLink's detectYtubeVimeo constructor parameter is gone), under one conservative rule: a link's explicitly declared type is never overridden.
  • KeepXmlDocument now defaults to false.
  • Mutable global statics (LocalIdTag, rel dictionaries, XSimpleFeed defaults) are gone or frozen.
  • A dozen+ long-standing bugs were fixed — several change output (enclosure Length/Height/Width now carry real values; more categories and authors survive parsing; declared encodings decode correctly). Every fix, with before/after code: docs/2026-08-21-v4-bug-fixes.md.

License

MIT

Downloads by Version

Similar & Alternative Packages

Hand-picked matching NuGet libraries based on tags & ecosystem

Depended On By

Find packages that declare this package as a dependency. NuGet does not provide a reverse-dependency endpoint, so candidates are checked on demand.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

Compatibility metadata lists 1 target framework; confirm the exact target used by your application.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.