Skip to content

Commit 4e304f4

Browse files
authored
Merge pull request #17 from jonsagara/release/2.0.0
BREAKING CHANGE: Category is now an object instead of a simple string
2 parents 9212871 + e90fea8 commit 4e304f4

File tree

10 files changed

+153
-20
lines changed

10 files changed

+153
-20
lines changed

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<Nullable>enable</Nullable>
99

1010
<!-- NuGet -->
11-
<Version>1.1.1</Version>
12-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
13-
<FileVersion>1.0.0.0</FileVersion>
11+
<Version>2.0.0</Version>
12+
<AssemblyVersion>2.0.0.0</AssemblyVersion>
13+
<FileVersion>2.0.0.0</FileVersion>
1414
<Authors>Armin Reiter; Jon Sagara</Authors>
1515
<Copyright>Copyright 2022-2024</Copyright>
1616
<RepositoryUrl>https://github.com/jonsagara/Sagara.FeedReader</RepositoryUrl>

src/Sagara.FeedReader.Tests.Unit/FullParseTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public async Task TestRss20ParseWebResourceSampleFull_Async()
246246
Assert.Equal("Mon, 30 Sep 2002 11:00:00 GMT", feed.LastBuildDateString);
247247
Assert.Equal("http://backend.userland.com/rss", feed.Docs);
248248
Assert.Equal("Radio UserLand v8.0.5", feed.Generator);
249-
Assert.Equal("1765", feed.Categories.First());
249+
Assert.Equal("1765", feed.Categories.Select(r2fc => r2fc.Content!).First());
250250
Assert.Equal("dave@userland.com", feed.ManagingEditor);
251251
Assert.Equal("dave@userland.com", feed.WebMaster);
252252
Assert.Equal("40", feed.TTL);
@@ -285,7 +285,7 @@ public async Task TestRss20ParseCodeHollow_Async()
285285
Assert.Equal(new DateTime(2016, 12, 22, 7, 0, 28), item.PublishingDate);
286286
Assert.Equal("Armin Reiter", item.DC!.Creator);
287287
Assert.Equal(4, item.Categories.Count);
288-
Assert.Contains("BillingAPI", item.Categories);
288+
Assert.Contains("BillingAPI", item.Categories.Select(r2fc => r2fc.Content!));
289289
Assert.Equal("https://codehollow.com/?p=749", item.Guid);
290290
Assert.StartsWith("<p>The Azure Billing API allows to programmatically read Azure", item.Description);
291291
Assert.Contains("<add key=\"Tenant\" ", item.Content);

src/Sagara.FeedReader/Extensions/ICollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public static class ICollectionExtensions
88
/// <summary>
99
/// Add a range of values to the collection.
1010
/// </summary>
11-
public static void AddRange<T>(this ICollection<T> source, IReadOnlyCollection<T> valuesToAdd)
11+
public static void AddRange<T>(this ICollection<T> source, IEnumerable<T> valuesToAdd)
1212
{
1313
ArgumentNullException.ThrowIfNull(source);
1414
ArgumentNullException.ThrowIfNull(valuesToAdd);

src/Sagara.FeedReader/Feeds/2.0/Rss20Feed.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class Rss20Feed : BaseFeed
6767
/// <summary>
6868
/// All "category" elements
6969
/// </summary>
70-
public IReadOnlyCollection<string> Categories { get; private set; } = Array.Empty<string>();
70+
public IReadOnlyCollection<Rss20FeedCategory> Categories { get; private set; } = Array.Empty<Rss20FeedCategory>();
7171

7272
/// <summary>
7373
/// The "generator" element
@@ -133,7 +133,11 @@ public Rss20Feed(string feedXml, XDocument feedDoc, XElement channel)
133133
LastBuildDateString = channel.GetChildElementValue("lastBuildDate");
134134
ParseDates(Language, PublishingDateString, LastBuildDateString);
135135

136-
Categories = channel.GetElements("category").Select(ce => ce.Value).ToArray();
136+
Categories = channel
137+
.GetElements("category")
138+
.Select(ce => new Rss20FeedCategory(ce))
139+
.Where(r2fc => !string.IsNullOrWhiteSpace(r2fc.Content))
140+
.ToArray();
137141

138142
Sy = new Syndication(channel);
139143
Generator = channel.GetChildElementValue("generator");
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.Xml.Linq;
2+
using Sagara.FeedReader.Extensions;
3+
4+
namespace Sagara.FeedReader.Feeds;
5+
6+
/// <summary>
7+
/// RSS 2.0 feed category according to specification: https://validator.w3.org/feed/docs/rss2.html
8+
/// </summary>
9+
public class Rss20FeedCategory : BaseFeedCategory
10+
{
11+
/// <summary>
12+
/// Optional. Identifies the taxonomy in which the category is placed. Comes from the domain attribute.
13+
/// </summary>
14+
public string? Domain { get; set; }
15+
16+
/// <summary>
17+
/// The category text pull from the tag's content.
18+
/// </summary>
19+
public string? Content { get; set; }
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="Rss20FeedCategory"/> class.
23+
/// default constructor (for serialization)
24+
/// </summary>
25+
public Rss20FeedCategory()
26+
: base()
27+
{
28+
}
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="Rss20FeedCategory"/> class.
32+
/// Reads a new feed category element based on the given xml item.
33+
/// </summary>
34+
/// <param name="categoryElement">The xml containing the feed item</param>
35+
public Rss20FeedCategory(XElement categoryElement)
36+
: base(categoryElement)
37+
{
38+
Domain = categoryElement.GetAttributeValue("domain");
39+
Content = categoryElement.Value;
40+
}
41+
}

src/Sagara.FeedReader/Feeds/2.0/Rss20FeedItem.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class Rss20FeedItem : BaseFeedItem
5353
/// <summary>
5454
/// All entries "category" entries
5555
/// </summary>
56-
public IReadOnlyCollection<string> Categories { get; private set; } = Array.Empty<string>();
56+
public IReadOnlyCollection<Rss20FeedCategory> Categories { get; private set; } = Array.Empty<Rss20FeedCategory>();
5757

5858
/// <summary>
5959
/// The "content:encoded" field
@@ -90,8 +90,11 @@ public Rss20FeedItem(XElement item)
9090
DC = new DublinCore(item);
9191
Source = new FeedItemSource(item.GetElement("source"));
9292

93-
var categories = item.GetElements("category");
94-
Categories = categories.Select(x => x.Value).ToArray();
93+
Categories = item
94+
.GetElements("category")
95+
.Select(ce => new Rss20FeedCategory(ce))
96+
.Where(r2fc => !string.IsNullOrWhiteSpace(r2fc.Content))
97+
.ToArray();
9598

9699
Guid = item.GetChildElementValue("guid");
97100
Description = item.GetChildElementValue("description");
@@ -111,7 +114,7 @@ internal override FeedItem ToFeedItem()
111114
PublishingDateString = PublishingDateString,
112115
};
113116

114-
fi.Categories.AddRange(Categories);
117+
fi.Categories.AddRange(Categories.Select(c => c.Content!));
115118

116119
return fi;
117120
}

src/Sagara.FeedReader/Feeds/Atom/AtomFeed.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class AtomFeed : BaseFeed
1616
/// <summary>
1717
/// All "category" elements
1818
/// </summary>
19-
public IReadOnlyCollection<string> Categories { get; private set; } = Array.Empty<string>();
19+
public IReadOnlyCollection<AtomFeedCategory> Categories { get; private set; } = Array.Empty<AtomFeedCategory>();
2020

2121
/// <summary>
2222
/// The "contributor" element
@@ -91,8 +91,11 @@ public AtomFeed(string feedXml, XDocument feedDoc, XElement feed)
9191

9292
Author = new AtomPerson(feed.GetElement("author"));
9393

94-
var categories = feed.GetElements("category");
95-
Categories = categories.Select(x => x.Value).ToArray();
94+
Categories = feed
95+
.GetElements("category")
96+
.Select(ce => new AtomFeedCategory(ce))
97+
.Where(afc => !string.IsNullOrWhiteSpace(afc.Term))
98+
.ToArray();
9699

97100
Contributor = new AtomPerson(feed.GetElement("contributor"));
98101
Generator = feed.GetChildElementValue("generator");
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Xml.Linq;
2+
using Sagara.FeedReader.Extensions;
3+
4+
namespace Sagara.FeedReader.Feeds;
5+
6+
/// <summary>
7+
/// Atom feed category according to specification: https://validator.w3.org/feed/docs/rss2.html
8+
/// </summary>
9+
public class AtomFeedCategory : BaseFeedCategory
10+
{
11+
/// <summary>
12+
/// The term attribute is a string that identifies the category to which the entry or feed belongs.
13+
/// </summary>
14+
public string? Term { get; set; }
15+
16+
/// <summary>
17+
/// Optional. Identifies the categorization scheme associated with this category. This attribute
18+
/// can be used, for example, to differentiate between tags and categories.
19+
/// </summary>
20+
public string? Scheme { get; set; }
21+
22+
/// <summary>
23+
/// Optional. Provides a human-readable label for display in end-user applications.
24+
/// </summary>
25+
public string? Label { get; set; }
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="AtomFeedCategory"/> class.
29+
/// default constructor (for serialization)
30+
/// </summary>
31+
public AtomFeedCategory()
32+
: base()
33+
{
34+
}
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="AtomFeedCategory"/> class.
38+
/// Reads a new feed category element based on the given xml item.
39+
/// </summary>
40+
/// <param name="categoryElement">The xml containing the feed item</param>
41+
public AtomFeedCategory(XElement categoryElement)
42+
: base(categoryElement)
43+
{
44+
Term = categoryElement.GetAttributeValue("term");
45+
Scheme = categoryElement.GetAttributeValue("scheme");
46+
Label = categoryElement.GetAttributeValue("label");
47+
}
48+
}

src/Sagara.FeedReader/Feeds/Atom/AtomFeedItem.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class AtomFeedItem : BaseFeedItem
1616
/// <summary>
1717
/// All "category" elements
1818
/// </summary>
19-
public IReadOnlyCollection<string> Categories { get; private set; } = Array.Empty<string>();
19+
public IReadOnlyCollection<AtomFeedCategory> Categories { get; private set; } = Array.Empty<AtomFeedCategory>();
2020

2121
/// <summary>
2222
/// The "content" element
@@ -96,9 +96,8 @@ public AtomFeedItem(XElement item)
9696

9797
Categories = item
9898
.GetElements("category")
99-
.Select(ce => ce.GetAttributeValue("term"))
100-
.Where(t => !string.IsNullOrWhiteSpace(t))
101-
.Select(t => t!)
99+
.Select(ce => new AtomFeedCategory(ce))
100+
.Where(afc => !string.IsNullOrWhiteSpace(afc.Term))
102101
.ToArray();
103102

104103
Content = item.GetChildElementValue("content").HtmlDecode();
@@ -133,7 +132,7 @@ internal override FeedItem ToFeedItem()
133132
PublishingDateString = PublishedDateString,
134133
};
135134

136-
fi.Categories.AddRange(Categories);
135+
fi.Categories.AddRange(Categories.Select(afc => afc.Term!));
137136

138137
return fi;
139138
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Xml.Linq;
2+
3+
namespace Sagara.FeedReader.Feeds;
4+
5+
/// <summary>
6+
/// The base object for all feed categories. Since the RSS 2.0 and Atom <c>category</c> elements differ,
7+
/// hydrating the object is done in feed-specific subclasses.
8+
/// </summary>
9+
public class BaseFeedCategory
10+
{
11+
/// <summary>
12+
/// The <c>Category</c> (RSS) or <c>entry</c> (Atom) element from the feed. Return as an XElement in order to
13+
/// allow reading properties that are not available as first-class properties in the derived class itself.
14+
/// </summary>
15+
public XElement? CategoryElement { get; }
16+
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="BaseFeedCategory"/> class.
20+
/// default constructor (for serialization)
21+
/// </summary>
22+
protected BaseFeedCategory()
23+
{
24+
}
25+
26+
/// <summary>
27+
/// Initializes a new instance of the <see cref="BaseFeedCategory"/> class.
28+
/// Reads a base feed item based on the xml given in element
29+
/// </summary>
30+
/// <param name="categoryElement">The <c>category</c> element from the feed.</param>
31+
public BaseFeedCategory(XElement categoryElement)
32+
{
33+
CategoryElement = categoryElement;
34+
}
35+
}

0 commit comments

Comments
 (0)