跟不上时代的人突然间走在了时代的前列,果然有别样的风景。首先鄙视一下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或者网页方式登录微博之后从服务器返回的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

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

{
"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。

to be continued…

 

Swift: 用Alamofire做http请求,用ObjectMapper解析JSON的更多相关文章

  1. 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.js ...

  2. andlua,andlua发送http请求,并解析json数据

    andlua发送http请求,并解析json实例 import'cjson'import 'http'--导入cjson库url = 'https://www.baidu,com'--设置urlHtt ...

  3. request模块做post请求时,body为json格式,并且带有中文,如何请求

    后台接口只能解析json,并且一定要是中文才能解析出来,如果是unicode编码的中文则会报错 看requests的源码.以下为解决方法: #将requests库中的models.py文件中的第461 ...

  4. Swift使用Alamofire实现网络请求

    Alamofire是一个用Swift编写的HTTP网络库,由此前热门开源项目AFNetworking的的作者mattt开发,可非常简单地用于异步网络通信. 要获取最新版本的 Alamofire,前往h ...

  5. Objective-C-使用NSMutableURLRequest发送POST请求,使用NSJSONSerialization解析JSON字符串

    NSString *reqData = @"Data="; NSData *postDatas = nil; NSString *urlPath = @"url" ...

  6. Objective-C——NSMutableURLRequest发送POST请求,使用NSJSONSerialization解析JSON字符串

    NSString *reqData = @"Data="; NSData *postDatas = nil; NSString *urlPath = @"url" ...

  7. Swift 网络请求数据与解析

    一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...

  8. 【swift学习笔记】四.swift使用Alamofire和swiftyJson

    Alamofire是AFNetworking的swift版本,功能灰常强大. github:https://github.com/Alamofire/Alamofire SwiftyJSON是操作js ...

  9. iOS开发——实战篇Swift篇&UItableView结合网络请求,多线程,数据解析,MVC实战

    UItableView结合网络请求,多线程,数据解析,MVC实战 学了这么久的swift都没有做过什么东西,今天就以自己的一个小小的联系,讲一下,怎么使用swift在实战中应用MVC,并且结合后面的高 ...

随机推荐

  1. centos7 二进制版本安装 mysql8.0.13

    一.下载mysql二进制安装包 mysql官网: https://dev.mysql.com/downloads/mysql/ 由于国内网络问题,个人建议使用国内开源镜像站下载: http://mir ...

  2. JFinal Web开发学习(一)开启HelloWorld

    初次接触JFinal框架,接触过MVC思想,接触过Spring. JFinal官网: http://www.jfinal.com/ 之前很嫌弃JavaWeb开发,主要原因是繁琐的xml配置. 官方推荐 ...

  3. 【MINA学习笔记】—— 1.体系结构分析[z]

    前言 Apache的MINA框架是一个早年非常流行的NIO框架,它出自于Netty之父Trustin Lee大神之手.虽然目前市场份额已经逐渐被Netty取代了,但是其作为NIO初学者入门学习框架是非 ...

  4. thymeleaf 格式化时间

    运用Thymeleaf模板后,前台的时间显示发生变化,和数据库不一致 HTML页面中格式如下: <td th:text="${fleeceRecord.cashmereDate}&qu ...

  5. c# 24种设计模式

    备忘录模式(Memento Pattern) 策略模式(Strategy Pattern) 抽象工厂模式(Abstract Factory Pattern) 代理模式(Proxy Pattern) 单 ...

  6. 多进程copy文件

    from multiprocessing import Pool,Manager import os,time def copyFileTask(fileName,oldFolderName,newF ...

  7. [Jmeter] Jmeter Plugins

    Plugins: Plugins Manager: https://jmeter-plugins.org/wiki/PluginsManager/ Custom Thread Groups: http ...

  8. JDK 之 Java Bean 内省机制

    JDK 之 Java Bean 内省机制 JDK 规范目录(https://www.cnblogs.com/binarylei/p/10200503.html) JavaBean 是一种特殊的 Jav ...

  9. 转:Struts2返回JSON数据的具体应用范例

    http://blog.csdn.net/jspamd/article/details/8810109 纠错: <result type="json" name=" ...

  10. merge_节点

    (1)CREATE可以创建相同节点,merge若节点已存在,则不会重新添加. CREATE (gp1:GoogleProfile1 {Id: 201401, Name:"Apple" ...