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

简介:

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

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  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 //1
Alamofire . request (. GET ""  // 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. 请求的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" "http : //weibo.com" rel="nofollow">新浪微博",
             "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  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格式是这样的:

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" "http : //weibo.com" rel="nofollow">新浪微博",
            "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  // 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属性也就可以给出一个正确的完整的写法了。

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 ]?  // 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类了。

1
2
3
4
5
6
7
8
9
10
11
12
parameters  = [ "access_token" weiboUserInfo . accessToken  ??  "" ,
                           "source" ConstantUtil . WEIBO_APPKEY ]
             Alamofire . request (. GET "" 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().map(response.result.value)方法来映射json串。这里需要分开来看。Mapper()初始化了一个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…



















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5170372.html,如需转载请自行联系原作者


相关文章
|
9月前
|
JSON 监控 API
掌握使用 requests 库发送各种 HTTP 请求和处理 API 响应
本课程全面讲解了使用 Python 的 requests 库进行 API 请求与响应处理,内容涵盖环境搭建、GET 与 POST 请求、参数传递、错误处理、请求头设置及实战项目开发。通过实例教学,学员可掌握基础到高级技巧,并完成天气查询应用等实际项目,适合初学者快速上手网络编程与 API 调用。
906 130
|
9月前
|
JSON 缓存 自然语言处理
多语言实时数据微店商品详情API:技术实现与JSON数据解析指南
通过以上技术实现与解析指南,开发者可高效构建支持多语言的实时商品详情系统,满足全球化电商场景需求。
|
10月前
|
JSON 算法 API
淘宝商品评论API接口核心解析,json数据返回
淘宝商品评论API是淘宝开放平台提供的数据服务接口,允许开发者通过编程方式获取指定商品的用户评价数据,包括文字、图片、视频评论及评分等。其核心价值在于:
|
8月前
|
JSON Java Go
【GoGin】(2)数据解析和绑定:结构体分析,包括JSON解析、form解析、URL解析,区分绑定的Bind方法
bind或bindXXX函数(后文中我们统一都叫bind函数)的作用就是将,以方便后续业务逻辑的处理。
473 3
|
8月前
|
XML JSON 数据处理
超越JSON:Python结构化数据处理模块全解析
本文深入解析Python中12个核心数据处理模块,涵盖csv、pandas、pickle、shelve、struct、configparser、xml、numpy、array、sqlite3和msgpack,覆盖表格处理、序列化、配置管理、科学计算等六大场景,结合真实案例与决策树,助你高效应对各类数据挑战。(238字)
1155 0
|
10月前
HTTP协议中请求方式GET 与 POST 什么区别 ?
GET和POST的主要区别在于参数传递方式、安全性和应用场景。GET通过URL传递参数,长度受限且安全性较低,适合获取数据;而POST通过请求体传递参数,安全性更高,适合提交数据。
881 2
|
9月前
|
JSON 自然语言处理 API
多语言实时数据淘宝商品评论API:技术实现与JSON数据解析指南
淘宝商品评论多语言实时采集需结合官方API与后处理技术实现。建议优先通过地域站点适配获取本地化评论,辅以机器翻译完成多语言转换。在合规前提下,企业可构建多语言评论数据库,支撑全球化市场分析与产品优化。
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
线程的状态有:new、runnable、running、waiting、timed_waiting、blocked、dead 当执行new Thread(Runnabler)后,新创建出来的线程处于new状态,这种线程不可能执行 当执行thread.start()后,线程处于runnable状态,这种情况下只要得到CPU,就可以开始执行了。
933 0
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
总结和计划总是让人喜悦或镇痛,一方面以前一段时间没有荒废,能给现在的行动以信心,另一方面看到一年的时间并不能完成很多事情,需要抓紧时间。
832 0
|
Web App开发 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
Every Programmer Should Know These Latency Numbers 1秒=1000毫秒(ms) 1秒=1,000,000 微秒(μs) 1秒=1,000,000,000 纳秒(ns) 1秒=1,000,000,000,000 皮秒(ps) L1 cache reference .
790 0

推荐镜像

更多
  • DNS