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

演示样例代码看最后。

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

另外一个必须的输入參数就是加入微博应用的时候生成的app key。

2. https://api.weibo.com/2/statuses/friends_timeline.json请求的url。

这个url返回的就是你follow的好友的微博。

就是你一打开微博client看到的那些。

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。

演示样例代码在这里。这里没有使用微博的API。而是用了Github的API来演示请求和JSON处理。

比較简单。

只是Github奇葩的返回的结果就是一个JSON Array,竟然能够使用ObjectMapper的mapArray方法一次搞定。这算是一个小坑。其它的都非经常规了。

to be continued…

原文地址:https://www.cnblogs.com/llguanli/p/8439300.html

时间: 2024-10-16 13:16:14

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

Swift: Alamofire -&gt; http请求 &amp; ObjectMapper -&gt; 解析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:^(NS

[Swift 工作tips] 之 使用Alamofire做网络请求时设置请求超时(timeout)时间

在应用开发过程中,经常需要网络请求,在网络请求的过程中,一般的第三方网络框架的超时时间比较长为15秒: 那么,我们如何来指定请求的超时时间呢? 在Swift的世界里,比较有名的网络是Alamofire   GitHut地址:https://github.com/Alamofire/Alamofire 那么,在使用Alamofire 的时候,设置Alamofire的请求时间如下: 本例代码如下: 1 var alamofireManager : Manager? 2 // 设置请求的超时时间 3

Swift使用Alamofire实现网络请求

Alamofire是一个用Swift编写的HTTP网络库,由此前热门开源项目AFNetworking的的作者mattt开发,可非常简单地用于异步网络通信. 要获取最新版本的 Alamofire,前往https://github.com/Alamofire/Alamofire然后单击网页右边的Download ZIP按钮.接着在 Finder 中打开起始项目文件夹,,然后将Alamofire-master文件夹拖入到您的主项目文件夹中. 打开Alamofire-master文件夹(现在它位于您的项

Swift 网络请求数据与解析

一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问题解决了,只是在发表的博客数量上 + 1 !!真心没意思.. 看看在Swift中是在怎样请求数据,解析数据加载图片这些的,也使我们最基本最常见的用法了,先说说这几个三方库: 第一个: Alamofire  (它的原作者就是AFNetworking的原作者,这个就不多说了,你要知道AFNetworki

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

UItableView结合网络请求,多线程,数据解析,MVC实战 学了这么久的swift都没有做过什么东西,今天就以自己的一个小小的联系,讲一下,怎么使用swift在实战中应用MVC,并且结合后面的高级知识:网络请求,JSON数据解析一起应用到一个项目中来. 好了,废话不多说,我们直接开始吧. 首先看看最终的效果: 是不是很简单,就是个UItableView显示一些简单的数据,如果你真的觉得太简单了,那么请绕道,寻找更深入东西,但或者没有你想的那么简单,这不仅仅是一个tableView,为什么呢

[Swift]使用Alamofire传递参数时报错

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 } span.s1 { } span.s2 { font: 11.0px "PingFang SC" } Swift使用Alamofire传递递参数时报错,会提示超时,主要错误信息如下: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }

iOS开发——网络Swift篇&amp;NSURL进行数据请求(POST与GET)

NSURL进行数据请求(POST与GET) 使用Swift进行iOS开发时,不可避免的要进行远程的数据获取和提交. 其数据请求的方式既可能是POST也可能是GET.同不管是POST还是GET又可以分为同步请求和异步请求. 下面通过四个例子来进行演示. 1,使用POST方式提交数据(用户id和分数) (1)同步请求 1 //保存分数 2 func saveScore(score:Int, userid:String) 3 { 4 let urlString:String = "http://han

python 使用 requests 做 http 请求

1. get import requests # 最简单的get请求 r = requests.get(url) print(r.status_code) print(r.json()) # url 中?key=value&key=value r = requests.get(url, params=params) # form 表单 params = {"username":"name", "password":"passw0

Shiro Ajax请求没有权限返回JSON,没有登录返回JSON

本文基于Shiro权限注解方式来控制Controller方法是否能够访问. 例如使用到注解: @RequiresPermissions 来控制是否有对应权限才可以访问 @RequiresUser 来控制是否存在用户登录状态才可以访问 想了解Shiro是如何通过注解来控制权限的,可以查看源码 AopAllianceAnnotationsAuthorizingMethodInterceptor ,其构造方法中添加了几个对应的权限注解方法拦截器(这里不做详细阐述). 用户在请求使用这些注解方式控制的方