• Swift: 用Alamofire做http请求,用ObjectMapper解析JSON


    演示样例代码看最后。
    

    跟不上时代的人突然间走在了时代的前列,果然有别样的风景。首先歧视一下AFNetworking。这个东西实在太难用了。不想封装都不行,要不写一大堆代码。

    NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"];
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager GET:URL.absoluteString parameters:nil 
        progress:nil 
        success:^(NSURLSessionTask *task, id responseObject) { 
            NSLog(@"JSON: %@", responseObject);
        } 
        failure:^(NSURLSessionTask *operation, NSError *error) {  
            NSLog(@"Error: %@", error);
        }
    ];

    Http请求

    可是用alamofire就简单的非常多了,如:

    Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) 
        .response { request, response, data, error in
             print(response) 
        }

    都是一个GET请求,可是可见的是Alamofire代码量少非常多。这也是和AFNetworking3.x比較了,假设你用的是AFNetworking2.x的话代码量的对照更加明显。对于程序猿来说调用方法的API简单方便就是用户体验。Developer们也是须要满足UE的须要的。

    以下開始进入正题。

    以下用请求微博的time line来做栗子。

    parameters = ["access_token": weiboUserInfo.accessToken ?? "",  "source": ConstantUtil.WEIBO_APPKEY] //1
    Alamofire.request(.GET, "https://api.weibo.com/2/statuses/friends_timeline.json" //2
        , parameters: parameters, encoding: .URL, headers: nil)
        .responseString(completionHandler: {response in
            print("response:- (response)") //3
    })

    这里用Alamofire请求微博的time line。
    1. 请求微博的time line就须要SSO或者网页方式登录微博之后从server返回的access_token。

    另外一个必须的输入參数就是加入微博应用的时候生成的app key。
    2. https://api.weibo.com/2/statuses/friends_timeline.json请求的url。
    这个url返回的就是你follow的好友的微博。

    就是你一打开微博client看到的那些。
    3. 我们知道Alamofire能够把请求返回的数据转化为JSON、String和NSData。

    假设是作为JSON来处理,也就是使用了responseJSON方法的话,JSON数据会被自己主动转化为NSDictionary

    我们后面须要用到字符串来实现json字符串和Model对象的匹配,所以我们用方法responseString

    假设一切设置正确,你会看到这种结果:

    {
        "statuses": [
            {
                "created_at": "Tue May 31 17:46:55 +0800 2011",
                "id": 11488058246,
                "text": "求关注。""source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>",
                "favorited": false,
                "truncated": false,
                "in_reply_to_status_id": "",
                "in_reply_to_user_id": "",
                "in_reply_to_screen_name": "",
                "geo": null,
                "mid": "5612814510546515491",
                "reposts_count": 8,
                "comments_count": 9,
                "annotations": [],
                "user": {
                    "id": 1404376560,
                    "screen_name": "zaku",
                    "name": "zaku",
                    "province": "11",
                    "city": "5",
                    "location": "北京 朝阳区",
                    "description": "人生五十年,乃如梦如幻;有生斯有死。壮士复何憾。",
                    "url": "http://blog.sina.com.cn/zaku",
                    "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1",
                    "domain": "zaku",
                    "gender": "m",
                    "followers_count": 1204,
                    ...
                }
            },
            ...
        ],
        "ad": [
            {
                "id": 3366614911586452,
                "mark": "AB21321XDFJJK"
            },
            ...
        ],
        "previous_cursor": 0,      // 临时不支持
        "next_cursor": 11488013766,     // 临时不支持
        "total_number": 81655
    }

    以上是微博给出来的样例的一部分,我们来看看我们须要什么。

    我们须要一部分文字和一部分的图片。之后要显示的内容主要就是文字或者图片。

    解析

    我们用ObjectMapper解析json。ObjectMapper是一个双向的转化工具。

    能够把json字符串转化成model也能够把model转化成json字符串。

    安装ObjectMapper:

    pod 'ObjectMapper', '~> 1.1'

    ObjectMapper对于json的解析都是从外往内进行的,这个层层解析的过程中一般没有特殊指定的话每一层都不能少(能够通过制定解析路径降低)。

    每一层都须要配备一个实体类。

    最外面的一层是:

    {
        "statuses": [
          ...
        ],
        "previous_cursor": 0,      
        "next_cursor": 11488013766,     
        "total_number": 81655
    }

    所以相应的model定义是这种:

    import ObjectMapper
    
    class BaseModel: Mappable {  // 1
        var previousCursor: Int?
        var nextCursor: Int?
        //var statuses 
        var totalNumber: Int?

    required init?(_ map: Map) { // 2 } func mapping(map: Map) { // 3 previousCursor <- map["previous_cursor"] nextCursor <- map["next_cursor"] //hasVisible <- map["hasvisible"] statuses <- map["..."] // 4 totalNumber <- map["total_number"] } }

    最重要的是先import ObjectMapper。没有这个什么都干不了。
    1. BaseModel类须要实现Mappable接口。后面就是这个protocol的实现。
    2. 返回可能为空对象的初始化方法,法临时用不到。
    3. 这种方法最关键了。在这种方法里指定json的值相应的是model里的哪个属性。这部分功能能够自己主动实现,哪位有心人能够fork出来写一个,也方便大家使用
    4. 请看下文。

    在深入一层

    上问的标签4的内容我们在这里具体介绍。我们要展示的内容都是在statuses下的。那么我们应该怎样处理这部分的内容呢?statuses的json格式是这种:

    {
        "statuses": [
          {
              "created_at": "Tue May 31 17:46:55 +0800 2011",
               "id": 11488058246,
               "text": "求关注。

    "。 "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>", "favorited": false, "truncated": false, "in_reply_to_status_id": "", "in_reply_to_user_id": "", "in_reply_to_screen_name": "", "geo": null, ... } ], }

    能够有两个方式来处理深层的json数据。一个是在mapping方法里指定json数据和属性的相应关系。

    比方在BaseMode类中映射statuses中的text能够这样写:

    class BaseModel {
      var text: String?
    
      required init?(_ map: Map) { 
      }
    
      func mapping(map: Map) {
        self.text <- map["statuses.text"]
      }
    }

    可是这样是错误的!由于statuses是一个数组,而不是一个对象。仅仅有statuses相应的是一个对象的时候才适用于这个情况。

    对上面的代码进行改动。让其适用于数据的情况。

    class BaseModel {
      var text: String?
    
      required init?

    (_ map: Map) { } func mapping(map: Map) { self.text <- map["status.0.text"] } }

    self.text <- map["statuses.0.text"]中间的数字说明text属性相应的是json中的statuses数组的第一个元素的text的值。可是在statuses下会有非常多个json对象。一个一个的挨个解析的方式显然是不适合的。

    更不用说这才两层,有多少奇葩的API返回的是三层甚至很多其它的?

    那么就剩下最后的一种方法了。内层json的model类继承外层的json的model类。依照这种方法那么我们为statuses相应的json对象定义一个model类为StatusModel。由于StatusModel相应的是内层的json对象,那么就须要继承外层的json对象的类,也就是BaseModel。刚開始就命名为BaseModel应该是已经露馅了。

    class StatusModel: BaseModel { // 1
        var statusId: String?
        var thumbnailPic: String?

    var bmiddlePic: String?

    var originalPic: String? var weiboText: String? var user: WBUserModel? required init?(_ map: Map) { super.init(map) // 2 } override func mapping(map: Map) { super.mapping(map) // 2 statusId <- map["id"] thumbnailPic <- map["thumbnail_pic"] bmiddlePic <- map["bmiddle_pic"] originalPic <- map["original_pic"] weiboText <- map["text"] } }

    1. 也就是我们说的json对象嵌套时的model类的继承关系。
    2. 在这种继承关系中须要十分注意的是。在Mappable协议的方法的调用中须要先调用基类的相应方法,super.init(map)super.mapping(map)

      至于说mapping方法的映射关系。每一个json对象相应的model类仅仅管这一个对象的就能够。

    那么在最外层的BaseModel类中的statuses属性也就能够给出一个正确的完整的写法了。

    class BaseModel: Mappable {
        var previousCursor: Int?

    var nextCursor: Int? var hasVisible: Bool? var statuses: [StatusModel]? // 1 var totalNumber: Int? required init?

    (_ map: Map) { } func mapping(map: Map) { previousCursor <- map["previous_cursor"] nextCursor <- map["next_cursor"] hasVisible <- map["hasvisible"] statuses <- map["statuses"] // 2 totalNumber <- map["total_number"] } }

    1. 内层的statuses数组直接调用内层json对象相应的model类的数组,也即是var statuses: [StatusModel]?
    2. mapping方法中指定属性和json对象的关系,这里是statuses <- map["statuses"]

    这样ObjectMapper就知道应该怎样解析json字符串到相应的类对象中了。除了上面提到的。ObjectMapper还有非常多其它的功能。

    假设须要了解很多其它能够查看官方文档

    那么从http请求,到返回数据。到解析json串的一系列动作就能够完整的联结起来了。

    最開始介绍使用Alamofire请求并成功返回之后。我们仅仅是把字符串打印了出来。

    如今能够调用map方法来匹配json串和我们定义好的model类了。

    parameters = ["access_token": weiboUserInfo.accessToken ??

    "", "source": ConstantUtil.WEIBO_APPKEY] Alamofire.request(.GET, "https://api.weibo.com/2/statuses/friends_timeline.json", parameters: parameters, encoding: .URL, headers: nil) .responseString(completionHandler: {response in print("response:- (response)") let statuses = Mapper<BaseModel>().map(response.result.value) // 1 print("total number: (statuses!.totalNumber)") if let timeLine = statuses where timeLine.totalNumber > 0 { // 2 self.timeLineStatus = timeLine.statuses self.collectionView?.reloadData() } })

    1. 使用Mapper<BaseModel>().map(response.result.value)方法来映射json串。

      这里须要分开来看。Mapper<BaseModel>()初始化了一个Mapper对象。Mapper是一个泛型。类型參数就是我们定义的最外层的json对象相应的model类BaseModel。之后我们调用了这个初始化好的Mapper对象的map方法。

      这种方法的參数就是一个json串,也就是字符串类型的,可是这个字符串必须是json格式的。

      response.result.value取出了http请求之后返回的json串。

    2. map方法返回的是可空类型的。

      所以须要用if-let的方式检查一下返回的值是否可用。

      在可用的情况下用where语句推断返回的timeLine总数是否大于零。

      大于零才是有意义的,才刷新collection view。

    演示样例代码在这里。这里没有使用微博的API。而是用了Github的API来演示请求和JSON处理。

    比較简单。

    只是Github奇葩的返回的结果就是一个JSON Array,竟然能够使用ObjectMapper的mapArray方法一次搞定。这算是一个小坑。其它的都非经常规了。

    to be continued…

  • 相关阅读:
    【Jenkins】坑
    【Windows】修改hosts
    【Linux】防火墙操作-centos7
    【Linux】学习-centos 7
    【Jenkins】介绍、安装
    【Jenkins】有关网站
    【Jenkins】中文汉化
    【Jenkins】修改Jenkins插件为国内源
    python笔记之孤单的元组
    python笔记之水仙花数
  • 原文地址:https://www.cnblogs.com/llguanli/p/8439300.html
Copyright © 2020-2023  润新知