• Swift: Alamofire -> http请求 & ObjectMapper -> 解析JSON


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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就简单的很多了,如:

    1
    2
    3
    4
    Alamofire.request(.GET"https://httpbin.org/get"parameters: ["foo""bar"])
        .response requestresponsedataerror in
             print(response)
        }

    都是一个GET请求,但是可见的是Alamofire代码量少很多。这也是和AFNetworking3.x比较了,如果你用的是AFNetworking2.x的话代码量的对比更加明显。对于程序员来说调用方法的API简单方便就是用户体验。Developer们也是需要满足UE的需要的。

    下面开始进入正题。下面用请求微博的time line来做栗子。

    1
    2
    3
    4
    5
    6
    parameters = ["access_token"weiboUserInfo.accessToken ?? "",  "source"ConstantUtil.WEIBO_APPKEY//1
        parametersparametersencoding: .URLheadersnil)
        .responseString(completionHandler: {response in
            print("response:- (response)"//3
    })

    这里用Alamofire请求微博的time line。 
    1. 请求微博的time line就需要SSO或者网页方式登录微博之后从服务器返回的access_token。另外一个必须的输入参数就是添加微博应用的时候生成的app key。 
    2. https://api.weibo.com/2/statuses/friends_timeline.json请求的url。 
    这个url返回的就是你follow的好友的微博。就是你一打开微博客户端看到的那些。 
    3. 我们知道Alamofire可以把请求返回的数据转化为JSON、String和NSData。如果是作为JSON来处理,也就是使用了responseJSON方法的话,JSON数据会被自动转化为NSDictionary。我们后面需要用到字符串来实现json字符串和Model对象的匹配,所以我们用方法responseString

    如果一切设置正确,你会看到这样的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    {
        "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的解析都是从外往内进行的,这个层层解析的过程中一般没有特殊指定的话每一层都不能少(可以通过制定解析路径减少)。每一层都需要配备一个实体类。

    最外面的一层是:

    1
    2
    3
    4
    5
    6
    7
    8
    {
        "statuses": [
          ...
        ],
        "previous_cursor"0,     
        "next_cursor"11488013766,    
        "total_number"81655
    }

    所以对应的model定义是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import ObjectMapper
     
    class BaseModelMappable {  // 1
        var previousCursorInt?
        var nextCursorInt?
        //var statuses
        var totalNumberInt?
     
        required init?(_ mapMap) {  // 2
     
        }
     
        func mapping(mapMap) { // 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格式是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {
        "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可以这样写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class BaseModel {
      var textString?
     
      required init?(_ mapMap) {
      }
     
      func mapping(mapMap) {
        self.text <map["statuses.text"]
      }
    }

    但是这样是错误的!因为statuses是一个数组,而不是一个对象。只有statuses对应的是一个对象的时候才适用于这个情况。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class BaseModel {
      var textString?
     
      required init?(_ mapMap) {
      }
     
      func mapping(mapMap) {
        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应该是已经露馅了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class StatusModelBaseModel // 1
        var statusIdString?
        var thumbnailPicString?
        var bmiddlePicString?
        var originalPicString?
        var weiboTextString?
        var userWBUserModel?
     
        required init?(_ mapMap) {
            super.init(map)  // 2
     
        }
     
        override func mapping(mapMap) {
            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属性也就可以给出一个正确的完整的写法了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class BaseModelMappable {
        var previousCursorInt?
        var nextCursorInt?
        var hasVisibleBool?
        var statuses: [StatusModel]? // 1
        var totalNumberInt?
     
        required init?(_ mapMap) {
     
        }
     
        func mapping(mapMap) {
            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还有很多其他的功能。如果需要了解更多可以查看官方文档

     或者是另外一种  方法二:

      StatusModel 不一定一定得继承自基类Model  ,这需要看是否有继承的需要(即是否包含相同字段)

      如果不是继承的关系: (mapping 写法)

        mutating func mapping(map: ObjectMapper.Map) {

       //blabla ...

           // settingDisplay <- map["settingDisplayList"]

            //settingItem <- map["settingItemList"]

        }

     

    或者是另外一种  方法三:

    如果是继承关系:   //外层的类是这样写   (mapping 写法)

    override func mapping(map: ObjectMapper.Map) {

          super.mapping(map)

       // blabla

    }

    基类里面像这样写:

        static func objectForMapping(map: Map) -> Mappable? {

            

            if let type = map["recordType"].currentValue as? String {

                

                switch type {

                    

                case "communicationNotebook":

                    return CommunicationNotebookModel(map)

                    

                case "dailyReport":

                    return DailyReportModel(map)

                case "photo":

                    return RecordPhotoModel(map)

                case "meal":

                    return MealModel(map)

                case "excretion":

                    return ExcretionModel(map)

                case "moisture":

                    return MoistureModel(map)

                case "snack":

                    return SnackModel(map)

                case "vital":

                    return VitalModel(map)

                case "drug":

                    return DrugModel(map)

                case "sleep":

                    return SleepModel(map)

                case "bathing":

                    return BathingModel(map)

                case "recreation":

                    return RecreationModel(map)

                case "outing":

                    return OutingModel(map)

                case "study":

                    return StudyModel(map)

                default:

                    return nil

                }

            }

            return nil

        }

    }

     

    外层的继承类像这样写:

    class CommunicationNotebookModel: RecordItemModel {         //内层jsonmodel类继承外层的jsonmodel

        

        /**

         内容

         */

        var content: String?

        

        var remarks: String?

       

        override func mapping(map: ObjectMapper.Map) {

            super.mapping(map)              //Mappable协议的方法的调用中需要先调用基类的对应方法,super.init(map)super.mapping(map)

            

            content <- map["content"]

            

            remarks <- map["remarks"]

        }

    }


    那么从http请求,到返回数据,到解析json串的一系列动作就可以完整的联结起来了。最开始介绍使用Alamofire请求并成功返回之后,我们只是把字符串打印了出来。现在可以调用map方法来匹配json串和我们定义好的model类了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    parameters = ["access_token"weiboUserInfo.accessToken ?? "",
                              "source"ConstantUtil.WEIBO_APPKEY]
                Alamofire.request(.GET"https://api.weibo.com/2/statuses/friends_timeline.json"parametersparametersencoding: .URLheadersnil)
                    .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

    参考链接:http://www.cnblogs.com/sunshine-anycall/p/5170372.html

  • 相关阅读:
    令Django 视图有默认 login_required
    令Django 视图有默认 login_required
    Python 资源
    Python 资源
    小米3 日历 同步google日历
    小米3 日历 同步google日历
    sql语句 case
    DDOSIM安装
    DDos工具tfn2k的编译
    RPM和YUM
  • 原文地址:https://www.cnblogs.com/Jenaral/p/5660348.html
Copyright © 2020-2023  润新知