博客园客户端(Universal App)开发随笔 -- 数据基础准备
在开始之前,我们先了解下博客园提供的接口:
博客: 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>//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