1
2
3
4
5
6
7
8
9
10
11
|
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
|
. response { request , response , data , error 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 ]
, parameters : parameters , encoding : . URL , headers : nil )
. responseString ( completionHandler : { response in
print ( "response:- \( response )" )
})
|
这里用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 :
"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" : "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。" ,
"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 BaseModel : Mappable {
var previousCursor : Int ?
var nextCursor : Int ?
var totalNumber : Int ?
required init ?( _ map : Map ) {
}
func mapping ( map : Map ) {
previousCursor < - map [ "previous_cursor" ]
nextCursor < - map [ "next_cursor" ]
statuses < - map [ "..." ]
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 :
"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 text : String ?
required init ?( _ map : Map ) {
}
func mapping ( map : Map ) {
self . text < - map [ "statuses.text" ]
}
}
|
但是这样是错误的!因为statuses是一个数组,而不是一个对象。只有statuses对应的是一个对象的时候才适用于这个情况。
对上面的代码进行修改,让其适用于数据的情况。
1
2
3
4
5
6
7
8
9
10
|
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 应该是已经露馅了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class StatusModel : BaseModel {
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 )
}
override func mapping ( map : Map ) {
super . mapping ( map )
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属性也就可以给出一个正确的完整的写法了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class BaseModel : Mappable {
var previousCursor : Int ?
var nextCursor : Int ?
var hasVisible : Bool ?
var statuses : [ StatusModel ]?
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" ]
totalNumber < - map [ "total_number" ]
}
}
|
- 内层的statuses数组直接调用内层json对象对应的model类的数组,也即是
var statuses: [StatusModel]? 。
- 在
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 |
请发表评论