在開始之前,我們先了解下博客園提供的接口:
博客: http://wcf.open.cnblogs.com/blog/help
新聞: http://wcf.open.cnblogs.com/news/help
以博客園_48小時閱讀排行為例,返回的Xml如下圖(RSS,如果你用IE打開的話,會提示你訂閱。。)。
博客園的大部分API返回的都是RSS(還提供分頁!),如果只是做一個簡單的RSS reader,可以直接用SyndicationClient,在RetrieveFeedAsync后會把XML解析成SyndicationFeed。
但是為了綁定方便和兼顧其他非RSS的API,我們的決定自定義實體類,然后用XmlSerializer反序列化,使用反序列化可以省去使用XmlDocument或者linq to xml解析的代碼。
建立工程:
因為我們准備做Universal App,這里我們建立一個支持可移植庫(Portable for Universal Apps)。
這個庫其實就是之前所謂的PCL,只不過默認支持的只有Windows 8.1和Windows phone8.1。
實體類
對於返回RSS的API,我們首先需要定義Feed<T>類,用來包含feed的基本信息,Entries屬性對應XML中所有的entry節點,之所以是泛型,是因為很多entry的屬性是不一樣的。
// Feed<T>對應的是feed節點,命名空間是必須的,否則找不到節點 [XmlRoot("feed", Namespace = "http://www.w3.org/2005/Atom")] public class Feed<T> { // Id屬性對應的是feed/id節點的值 [XmlElement("id")] public string Id { get; set; } [XmlElement("title")] public string Title { get; set; } // XmlSerializer會把feed下所有entry節點解析成對應的實體,然后放入List [XmlElement("entry")] public List<T> Entries { get; set; } }
有了Feed<T> 之后,針對不同的API定義實體類, 如推薦博主的Blogger。
// 這個root 就不需要namespace了,因為Feed已經有了,反序列化的時候使用Feed<Blogger>即可 [XmlRoot("entry")] public class Blogger { [XmlElement("id")] public string Id { get; set; } [XmlElement("title")] public string Name { get; set; } [XmlElement("updated")] public string UpdateTimeString { get; set; } [XmlElement("link", typeof(Link))] public Link Link { get; set; } [XmlElement("blogapp")] public string BlogApp { get; set; } [XmlElement("avatar")] public string Avatar { get; set; } [XmlElement("postcount")] public string PostCount { get; set; } [XmlIgnore] public DateTime UpdateTime { get { return Functions.ParseDateTime(this.UpdateTimeString); } } }
對於新聞和博客,大部分屬性都是一樣的,所以我們定義了一個EntryBase基類(起名字什么的最頭疼了。。),其他的可以參考我們的github上源代碼,地址在文章末尾處。
[XmlRoot(ElementName = "entry")] public class EntryBase { [XmlElement("id")] public string ID { get; set; } [XmlElement("title")] public string Title { get; set; } [XmlElement("summary")] public string Summary { get; set; } [XmlElement("published")] public string PublishTimeString { get; set; } [XmlElement("link", typeof(Link))] public Link Link { get; set; } [XmlElement("blogapp")] public string BlogApp { get; set; } [XmlElement("diggs")] public string DiggsCount { get; set; } [XmlElement("views")] public string ViewsCount { get; set; } [XmlElement("comments")] public string CommentsCount { get; set; } [XmlIgnore] public PostStatus Status{ get; set; } [XmlIgnore] public DateTime PublishTime { get { return Functions.ParseDateTime(this.PublishTimeString); } } }
其中UpdateTime需要注意的是,博客園的部分API返回的DateTime字符串在反序列化時會出錯(應該是后面少了時區什么的),所以這里直接用的是string(這里偷懶了。。),然后應用內使用的時候自己解析。
非RSS的新聞內容:
XmlElementAttribute沒有寫節點名稱的話,說明節點名稱和屬性名是一樣(包括大小寫),如下面這段Xml。
<?xml version="1.0" encoding="utf-8"?><NewsBody xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Title>90后創業者孫宇晨:我衡量人的標准就是你賺多少錢</Title><SourceName>投資界</SourceName><SubmitDate>2014-12-15 16:30:59</SubmitDate><Content>我現在衡量別人的標准,這個人為社會...</Content><ImageUrl>https://images0.cnblogs.com/news/66372/201412/151630299226167.jpg</ImageUrl><PrevNews>510939</PrevNews><NextNews/><CommentCount>0</CommentCount></NewsBody>
public class NewsBody
{
[XmlElement]
public string SubmitDateString { get; set; }
[XmlElement]
public string Content { get; set; }
[XmlElement]
public string ImageUrl { get; set; }
[XmlElement]
public string PrevNews { get; set; }
[XmlElement]
public string NextNews { get; set; }
[XmlElement]
public string CommentCount { get; set; }
[XmlElement]
public string Title { get; set; }
[XmlIgnore]
public DateTime SubmitDate
{
get {
return Functions.ParseDateTime(this.SubmitDateString);
}
}
}
public class NewsBody { [XmlElement] public string SubmitDateString { get; set; } [XmlElement] public string Content { get; set; } [XmlElement] public string ImageUrl { get; set; } [XmlElement] public string PrevNews { get; set; } [XmlElement] public string NextNews { get; set; } [XmlElement] public string CommentCount { get; set; } [XmlElement] public string Title { get; set; } [XmlIgnore] public DateTime SubmitDate { get { return Functions.ParseDateTime(this.SubmitDateString); } } }
Http數據請求
目前winrt中可以用來實現Http數據請求的類至少有3個:WebRequest,Windows.Web.Http.HttpClient和System.Net.Http.HttpClient和System.Net.Http.HttpClient。
建議使用Windows.Web.Http.HttpClient這個新加的類,另一個HttpClient可能在以后的某個版本中被砍掉,MSDN對於這點有專門的提醒,至於WebRequest使用則起來沒有新的HttpClient方便。
HttpClient提供常用的Http請求方法:GetAsync, DeleteAsync, PostAsync和GetStringAsync,其中GetStringAsync用來請求XML/Json時相當方便,省去了從response.content轉成string的過程(但是也得不到HttpStatusCode了,有得有失。。),使用如下:
HttpClient client = new HttpClient(); try { string xmlString = await client.GetStringAsync(new Uri(“API URL here”)); } catch(Exception) { //網絡不可用時會拋出異常 }
數據請求就是這么簡單,剩下的就是根據不同需求來拼接API的URL。
PS:當網絡不可用的時候,請求會拋出System.Exception,帶着錯誤代碼,切記要捕獲這個異常。
小結
使用HttpClient獲得Xml數據是非常簡單的,而有了XmlSerializer更可以把從Xml到實體類的映射過程簡化。歡迎大家繼續關注。
Windows Phone Store App link:
http://www.windowsphone.com/zh-cn/store/app/博客園-uap/500f08f0-5be8-4723-aff9-a397beee52fc
Windows Store App link:
http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059
GitHub open source link:
https://github.com/MS-UAP/cnblogs-UAP
MSDN Sample Code:
https://code.msdn.microsoft.com/CNBlogs-Client-Universal-477943ab