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

Swift: 用Alamofire做http请求,用ObjectMapper解析JSON,第1张

概述示例代码看最后。 跟不上时代的人突然间走在了时代的前列,果然有别样的风景。首先鄙视一下AFNetworking。这个东西实在太难用了。不想封装都不行,要不写一大堆代码。 NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"];AFHTTPSessionManager *manager = [AFHTT
示例代码看最后。

跟不上时代的人突然间走在了时代的前列,果然有别样的风景。首先鄙视一下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] //1Alamofire.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。

请求微博的time line就需要SSO或者网页方式登录微博之后从服务器返回的access_token。另外一个必须的输入参数就是添加微博应用的时候生成的app key。

https://api.weibo.com/2/statu...请求的url。
这个url返回的就是你follow的好友的微博。就是你一打开微博客户端看到的那些。

我们知道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": [      ...    ],"next_cursor": 11488013766,"total_number": 81655}

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

import ObjectMapperclass 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。没有这个什么都干不了。

BaseModel类需要实现Mappable接口。后面就是这个protocol的实现。

返回可能为空对象的初始化方法,法暂时用不到。

这个方法最关键了。在这个方法里指定Json的值对应的是model里的哪个属性。这部分功能可以自动实现,哪位有心人可以fork出来写一个,也方便大家使用

请看下文。

在深入一层

上问的标签4的内容我们在这里详细介绍。我们要展示的内容都是在statuses下的。那么我们应该如何处理这部分的内容呢?statuses的Json格式是这样的:

{    "statuses": [      {          "created_at": "Tue May 31 17:46:55 +0800 2011","text": "求关注。",           "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>",...      }    ],}

可以有两个方式来处理深层的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"]    }}

也就是我们说的Json对象嵌套时的model类的继承关系。

在这种继承关系中需要十分注意的是。在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"]    }}

内层的statuses数组直接调用内层Json对象对应的model类的数组,也即是 var statuses: [StatusModel]?

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",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()                    }            })

使用Mapper<BaseModel>().map(response.result.value)方法来映射Json串。这里需要分开来看。Mapper<BaseModel>()初始化了一个Mapper对象。Mapper是一个泛型,类型参数就是我们定义的最外层的Json对象对应的model类BaseModel。之后我们调用了这个初始化好的Mapper对象的map方法。这个方法的参数就是一个Json串,也就是字符串类型的,但是这个字符串必须是Json格式的。response.result.value取出了http请求之后返回的Json串。

map方法返回的是可空类型的。所以需要用if-let的方式检查一下返回的值是否可用。在可用的情况下用where语句判断返回的timeline总数是否大于零。大于零才是有意义的,才刷新collection vIEw。

示例代码在这里。这里没有使用微博的API,而是用了Github的API来演示请求和JsON处理。比较简单。不过Github奇葩的返回的结果就是一个JsON Array,居然可以使用ObjectMapper的mapArray方法一次搞定。这算是一个小坑。其他的都很常规了。

to be continued... 总结

以上是内存溢出为你收集整理的Swift: 用Alamofire做http请求,用ObjectMapper解析JSON全部内容,希望文章能够帮你解决Swift: 用Alamofire做http请求,用ObjectMapper解析JSON所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1073877.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存