博客园客户端(Universal App)开发随笔 -- 数据基础准备

在开始之前,我们先了解下博客园提供的接口:

博客: https://wcf.open.cnblogs.com/blog/help
新闻: https://wcf.open.cnblogs.com/news/help

博客园_48小时阅读排行为例,返回的Xml如下图(RSS,如果你用IE打开的话,会提示你订阅。。)。

博客园_48小时阅读排行

博客园的大部分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 = "https://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="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"><Title>90后创业者孙宇晨:我衡量人的标准就是你赚多少钱</Title><SourceName>投资界</SourceName><SubmitDate>2014-12-15 16:30:59</SubmitDate><Content>我现在衡量别人的标准,这个人为社会...</Content><ImageUrl>https://images.cnitblog.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:

https://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

 

Windows Store App link:

https://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