IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    swift中优雅的处理JSON

    shendao发表于 2017-04-01 18:13:09
    love 0

    声明:本文中部分引用了喵神博客中关于JSON处理的举例

    在iOS开发中,只要你的APP和网络打交道,那么基本上处理JSON是必须的。

    在Objective – C里面处理JSON就非常方便了,你可以随意的使用各方大神的开源库(YYModel,Mantle,MJExtension…)来处理,整个过程无痛无氧,顺滑如丝,因为Objective – C可以使用runtime非常自由。

    举个例子:

    // jsonString ------ 引用自喵神的博客 {"menu": {     "id": "file",     "value": "File",     "popup": {         "menuitem": [             {"value": "New", "onclick": "CreateNewDoc()"},             {"value": "Open", "onclick": "OpenDoc()"},             {"value": "Close", "onclick": "CloseDoc()"}         ]     } }}

    在Objective – C,如果你使用开源库来解析,非常简单,一句话:

    Menu *menu = [Menu yy_modelWithJSON:json];

    这么一句话,不但帮你把复杂的层级关系解析好了,而且做好对应model的映射赋值,这功能简直了,那么我们看看在swift中是如何处理的,代码如下:

    // 访问menuitem中的某一个value的值 ------ 引用自喵神的博客 if let jsonDic = json as? NSDictionary,           menu = jsonDic["menu"] as? [String: AnyObject],          popup = menu["popup"],       popupDic = popup as? [String: AnyObject],      menuItems = popupDic["menuitem"],   menuItemsArr = menuItems as? [AnyObject],          item0 = menuItemsArr[0] as? [String: AnyObject],          value = item0["value"] {     print(value) }

    有童鞋可能会觉得,这还好呀,代码整齐,格式美观

    可是真的是这样么?难道你家的json都是这么短的 ?我想你此刻已经知道我要说啥了,对,就是那个该死的后台,一眼看去望不到边的json,随便往工具里面转化一个,差不多两屏…

    咱们打死他可好?

    且慢!

    当然啦,在swift也有响应的JSON解析开源库可用,比如SwiftyJSON,而且看起来非常6,乍一看简直上天,来来来,你们感受一下

    // 使用 SwiftJSON ------ 引用自喵神的博客 if let value = JSON(json)["menu"]["popup"]["menuitem"][0]["value"].string {     print(value) }

    屌炸天有木有,自由的无限引用。有些同学可能会瞬间想到一个问题:我靠,这样以后是不是不用写model了?哪里需要,哪里就会出现JSON[“xxx”]。

    swift中优雅的处理JSON

    这个问题,很多人讨论过,这样看起来没有任何问题,反正可以取到json值,怕个鸟,写呗。

    但是这样真的好么?没有了model,维护成本真的是成倍增加的,你让后来的维护同学怎么玩?再去撸一遍后台同学的开发文档?那个能把json写成几页长度的童鞋写出来的文档,你确定他能看下去?就算看下去了,你确定能看懂?就算看懂了,你确定他不会骂你?

    好吧,还是乖乖的写Model吧。

    那么问题来了,写了model,但是SwiftyJSON这货只能把json解析出来,不能自动映射到你的model实例上去。什么?你没有听懂!

    呃…

    好吧,是这样的,比如:

    let model = TestModel() // 如果想让这个model里面有东西,你还得赋值/初始化  // 初始化/赋值 model.a = JSON["a"] model.b = JSON["b"] model.c = JSON["c"] model.d = JSON["d"] ... // 以下省略两页纸

    我的天呐…

    要死人么 ?

    swift中优雅的处理JSON
    为了这个问题,我也曾苦恼过,我也曾痛苦过…

    在github上搜索了N久,也问过各路大神(有些大神同学居然有setValueForKey,好吧,也是个方法).

    后来找到一个可以自动匹配的库ObjectMapper,终于春天来临了…

    ObjectMapper使用大致如下:

    class User: Mappable {     var username: String?     var age: Int?     var weight: Double!     var array: [AnyObject]?     var dictionary: [String : AnyObject] = [:]     var bestFriend: User?                       // Nested User object     var friends: [User]?                        // Array of Users     var birthday: NSDate?      required init?(_ map: Map) {      }      // Mappable     func mapping(map: Map) {         username    <- map["username"]         age         <- map["age"]         weight      <- map["weight"]         array       <- map["arr"]         dictionary  <- map["dict"]         bestFriend  <- map["best_friend"]         friends     <- map["friends"]         birthday    <- (map["birthday"], DateTransform())     } }  struct Temperature: Mappable {     var celsius: Double?     var fahrenheit: Double?      init?(_ map: Map) {      }      mutating func mapping(map: Map) {         celsius     <- map["celsius"]         fahrenheit  <- map["fahrenheit"]     } }  // 然后这样一句话就可以自动映射了 let user = Mapper<User>().map(JSONString)

    其实,比起YYModel等库,还是麻烦了不少,程序员嘛,总会有办法。

    后来我发现了一个好东西,SwiftyJSONAccelerator,这家伙有点儿类似以前的一个Xcode插件ESJsonFormator,对,你把自己的json串放进去,他能自动给你生成model,并且各中匹配都给你写好自动生成

    swift中优雅的处理JSON
    SwiftyJSONAccelerator

    哇偶,感觉瞬间翻身解放了.

    等等,并未解放,ObjectMapper还有一个蛋疼的问题。

    那就是,这个东西,你必须把整个json都写好,然后让他去匹配,那如果只需要一部分怎么办?

    比如,外围的数据其实不需要,我们只要body或者data字段下的东西…

    我的天呐,简直有点儿傻那啥(你们说的哈,我没有说…)

    为了解决这个问题,我github上各种搜,未果…

    后来只能乖乖的回去研究他的源码,看看能不能改。

    然而并没有,后来在AlamofireObjectMapper里面发现提供了一个keyPath的方法:

    // 使用了AlamofireObjectMapper以后,你可以这样玩 Alamofire.request(.GET, URL).responseObject(keyPath: "body") { (response: Response<WeatherResponse, NSError>) in      let weatherResponse = response.result.value     print(weatherResponse?.location)      if let threeDayForecast = weatherResponse?.threeDayForecast {         for forecast in threeDayForecast {             print(forecast.day)             print(forecast.temperature)                    }     } }

    哈哈,这样就好多了,很多不需要的外围字段,可以过滤掉。

    但,这样感觉相对没有SwiftyJSON那种牛逼烘烘的自由度了,于是又开始各种折腾。

    在SwiftyJSON里面有提供这样API:

    // 通过这个API,你可以把你得到的JSON转换成源生的jsonString public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String?

    有的小伙伴估计已经想到了,对的

    让SwiftyJSON和ObjectMapper组合起来,使用SwiftyJSON取到任何自己想取的数据字段,然后去map到Model上去,是不是就完美了!

    所以网络请求就封装成了下面这样:

    func postForObject<T: Mappable>(objectType type: T.Type,                                             path: String,                                       parameters: [String : AnyObject]?,                                         finished: (result: T?, error: NSError? ) -> Void) {         Alamofire.request(.POST, baseUrl + path, parameters: parameters).responseJSON { response in             let json = JSON(data: response.data!)             if json["status"].int == 200 {                 if let error = json["body"].error {                     finished(result: nil, error: error)                 } else {                     let rawJson = json["body"].rawString(NSUTF8StringEncoding, options: .PrettyPrinted)                     let obj = Mapper<T>().map(rawJson!)                     finished(result: obj, error: nil)                 }             } else {                 finished(result: nil, error: nil)             }         }     }

    如果有一些model数据量很小,你都不想写model了,你也可以再开放一个纯JSON的API,大致如下:

    func postForJSON(path: String, parameters: [String : AnyObject]?, finished: (result: JSON?, error: NSError?) -> Void) {         Alamofire.request(.POST, baseUrl + path, parameters: parameters).responseJSON { response in             let json = JSON(data: response.data!)             if json["status"].int == 200 {                 if let error = json["body"].error {                     finished(result: nil, error: error)                 } else {                     finished(result: json["body"], error: nil)                 }             } else {                 finished(result: nil, error: nil)             }         }     }

    如果错误欢迎讨论…
    生命不息,折腾不止…
    I’m not a real coder,but i love it so much!



沪ICP备19023445号-2号
友情链接