Xamarin.Forms で RSS フィードを取得したい
今年も開催される予定らしい Microsoft MVP Global Summit にせっかくなので参加しようと思っているのですが、心配なのが英語・・・
流れてくる情報のほとんどが英語のため、理解できないとだいぶ損をしてしまうことは必至
ということで勉強もかねて TechCrunch 英語版のニュースリーダーアプリを Xamarin で作ってみたいと思います
まずは RSS フィードの読み取りから
WinRT の場合 SyndicationClient という便利クラスがあるのですが、残念ながら PCL ではありません
そのため簡易的な読み取り処理クラスを作ってみます
NuGet で下記の Microsoft.Net.Http のパッケージ をインストールしてから下記のコードを書きます
/// <summary> /// News feed management service class /// </summary> public class NewsFeedService { /// <summary> /// Feed endpoint uri /// </summary> private static readonly string Endpoint = @"http://feeds.feedburner.com/TechCrunch/"; /// <summary> /// Feed items /// </summary> public Rss Feed { get; set; } /// <summary> /// Constructor /// </summary> public NewsFeedService() { this.Feed = new Rss(); } /// <summary> /// Update feeds /// </summary> /// <returns>Task</returns> public async Task Update() { var client = new HttpClient(); Rss latest; using (var reader = new StringReader(await client.GetStringAsync(Endpoint))) { var desirializer = new XmlSerializer(typeof(Rss)); latest = desirializer.Deserialize(reader) as Rss; } if (latest == null || latest.Channel.LastBuildDate.CompareTo(this.Feed.Channel.LastBuildDate) <= 0) { return; } // Merge old feed items and new feed items var oldItems = (from f in this.Feed.Channel.Items where !latest.Channel.Items.Any(l => l.Equals(f.Guid)) orderby f.PubDate descending select f).ToList(); foreach (var old in oldItems) { latest.Channel.Items.Add(old); } this.Feed = latest; } }
やっていることは RSS の Uri から XML をダウンロードしてデシリアライズしているだけ
ほんとは HttpClient.GetStreamAsync() でバッファリングしたいところですが、Windows Phone だと NotSupportedException が起きたので、仕方なく一気にダウンロードしています
入れものになっている RSS クラス配下のエンティティは次のような感じ
/// <summary> /// RSS 2.0 enitity root class /// </summary> [XmlRoot("rss")] public class Rss { /// <summary> /// RSS channel /// </summary> [XmlElement("channel")] public RssChannel Channel { get; set; } /// <summary> /// Constructor /// </summary> public Rss() { this.Channel = new RssChannel(); } } /// <summary> /// RSS channel entity class /// </summary> public class RssChannel { /// <summary> /// Title /// </summary> [XmlElement("title")] public string Title { get; set; } /// <summary> /// Description /// </summary> [XmlElement("description")] public string Description { get; set; } /// <summary> /// Link uri /// </summary> [XmlElement("link")] public string Link { get; set; } /// <summary> /// Last update datetime /// </summary> [XmlIgnore] public DateTime LastBuildDate { get; set; } /// <summary> /// Serializable last update datetime /// </summary> [XmlElement("lastBuildDate")] public string LastBuildDateString { get { return this.LastBuildDate.ToString(); } set { DateTime date; if (DateTime.TryParse(value, out date)) { this.LastBuildDate = date; } } } /// <summary> /// Editor /// </summary> [XmlElement("managingEditor")] public string ManagingEditor { get; set; } /// <summary> /// Feed items /// </summary> [XmlElement("item")] public List<RssItem> Items { get; set; } /// <summary> /// Constructor /// </summary> public RssChannel() { this.Items = new List<RssItem>(); this.LastBuildDate = DateTime.MinValue; } } /// <summary> /// RSS feed item entity class /// </summary> public class RssItem { /// <summary> /// Title /// </summary> [XmlElement("title")] public string Title { get; set; } /// <summary> /// Link uri /// </summary> [XmlElement("link")] public string Link { get; set; } /// <summary> /// Item ID /// </summary> [XmlElement("guid")] public string Guid { get; set; } /// <summary> /// Publish datetime /// </summary> [XmlIgnore] public DateTime PubDate { get; set; } /// <summary> /// Serializable publish datetime /// </summary> [XmlElement("pubDate")] public string PubDateString { get { return this.PubDate.ToString(); } set { DateTime date; if (DateTime.TryParse(value, out date)) { this.PubDate = date; } } } /// <summary> /// Description /// </summary> [XmlElement("description")] public string Description { get; set; } /// <summary> /// Description /// </summary> [XmlElement("category")] public List<string> Category { get; set; } /// <summary> /// Constructor /// </summary> public RssItem() { this.Category = new List<string>(); } }
DateTime 型のプロパティだけちょっと特別なことをしていますが基本的に XmlSerializer 用の属性をつけているだけです
さて、ちゃんと TechCrunch の RSS フィードがデシリアライズできるか試してみます
/// <summary> /// Top page /// </summary> public partial class TopPage : ContentPage { /// <summary> /// Constructir /// </summary> public TopPage() { InitializeComponent(); } /// <summary> /// Appearing event handler /// </summary> protected override async void OnAppearing() { base.OnAppearing(); await new NewsFeedService().Update(); } }
最初の画面のコードビハインドで上記のようなコードを書いて実行
日付文字列もちゃんと DateTime 型に変換できていますね
Description の中身は HTML のようです・・・これをどう表示しようか;