swift中優(yōu)雅的處理JSON

聲明:本文中部分引用了喵神博客中關(guān)于JSON處理的舉例

在iOS開發(fā)中,只要你的APP和網(wǎng)絡(luò)打交道,那么基本上處理JSON是必須的。

在Objective - C里面處理JSON就非常方便了,你可以隨意的使用各方大神的開源庫(YYModel,Mantle,MJExtension...)來處理,整個(gè)過程無痛無氧,順滑如絲,因?yàn)镺bjective - C可以使用runtime非常自由。

舉個(gè)例子:

// 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];

這么一句話,不但幫你把復(fù)雜的層級(jí)關(guān)系解析好了,而且做好對(duì)應(yīng)model的映射賦值,這功能簡直了,那么我們看看在swift中是如何處理的,代碼如下:

// 訪問menuitem中的某一個(gè)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)
}

有童鞋可能會(huì)覺得,這還好呀,代碼整齊,格式美觀

可是真的是這樣么?難道你家的json都是這么短的 ?我想你此刻已經(jīng)知道我要說啥了,對(duì),就是那個(gè)該死的后臺(tái),一眼看去望不到邊的json,隨便往工具里面轉(zhuǎn)化一個(gè),差不多兩屏…

咱們打死他可好?

且慢!

當(dāng)然啦,在swift也有相應(yīng)的JSON解析開源庫可用,比如SwiftyJSON,而且看起來非常6,乍一看簡直上天,來來來,你們感受一下

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

屌炸天有木有,自由的無限引用。有些同學(xué)可能會(huì)瞬間想到一個(gè)問題:我靠,這樣以后是不是不用寫model了?哪里需要,哪里就會(huì)出現(xiàn)JSON["xxx”]。

這個(gè)問題,很多人討論過,這樣看起來沒有任何問題,反正可以取到j(luò)son值,怕個(gè)鳥,寫唄。

但是這樣真的好么?沒有了model,維護(hù)成本真的是成倍增加的,你讓后來的維護(hù)同學(xué)怎么玩?再去擼一遍后臺(tái)同學(xué)的開發(fā)文檔?那個(gè)能把json寫成幾頁長度的童鞋寫出來的文檔,你確定他能看下去?就算看下去了,你確定能看懂?就算看懂了,你確定他不會(huì)罵你?

好吧,還是乖乖的寫Model吧。

那么問題來了,寫了model,但是SwiftyJSON這貨只能把json解析出來,不能自動(dòng)映射到你的model實(shí)例上去。什么?你沒有聽懂!

呃...

好吧,是這樣的,比如:

let model = TestModel() // 如果想讓這個(gè)model里面有東西,你還得賦值/初始化

// 初始化/賦值
model.a = JSON["a"]
model.b = JSON["b"]
model.c = JSON["c"]
model.d = JSON["d"]
... // 以下省略兩頁紙

我的天吶...

要死人么 ?


為了這個(gè)問題,我也曾苦惱過,我也曾痛苦過…

在github上搜索了N久,也問過各路大神(有些大神同學(xué)居然有setValueForKey,好吧,也是個(gè)方法).

后來找到一個(gè)可以自動(dòng)匹配的庫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"]
    }
}

// 然后這樣一句話就可以自動(dòng)映射了
let user = Mapper<User>().map(JSONString)

其實(shí),比起YYModel等庫,還是麻煩了不少,程序員嘛,總會(huì)有辦法。

后來我發(fā)現(xiàn)了一個(gè)好東西,SwiftyJSONAccelerator,這家伙有點(diǎn)兒類似以前的一個(gè)Xcode插件ESJsonFormator,對(duì),你把自己的json串放進(jìn)去,他能自動(dòng)給你生成model,并且各中匹配都給你寫好自動(dòng)生成

SwiftyJSONAccelerator

哇偶,感覺瞬間翻身解放了.

等等,并未解放,ObjectMapper還有一個(gè)蛋疼的問題。

那就是,這個(gè)東西,你必須把整個(gè)json都寫好,然后讓他去匹配,那如果只需要一部分怎么辦?

比如,外圍的數(shù)據(jù)其實(shí)不需要,我們只要body或者data字段下的東西...

我的天吶,簡直有點(diǎn)兒傻那啥(你們說的哈,我沒有說…)

為了解決這個(gè)問題,我github上各種搜,未果…

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

然而并沒有,后來在AlamofireObjectMapper里面發(fā)現(xiàn)提供了一個(gè)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)           
        }
    }
}

哈哈,這樣就好多了,很多不需要的外圍字段,可以過濾掉。

但,這樣感覺相對(duì)沒有SwiftyJSON那種牛逼烘烘的自由度了,于是又開始各種折騰。

SwiftyJSON里面有提供這樣API:

// 通過這個(gè)API,你可以把你得到的JSON轉(zhuǎn)換成源生的jsonString
public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String?

有的小伙伴估計(jì)已經(jīng)想到了,對(duì)的

SwiftyJSONObjectMapper組合起來,使用SwiftyJSON取到任何自己想取的數(shù)據(jù)字段,然后去map到Model上去,是不是就完美了!

所以網(wǎng)絡(luò)請(qǐng)求就封裝成了下面這樣:

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數(shù)據(jù)量很小,你都不想寫model了,你也可以再開放一個(gè)純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)
            }
        }
    }

如果錯(cuò)誤歡迎討論...
生命不息,折騰不止...
I'm not a real coder,but i love it so much!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容