1. 程式人生 > >SwiftyJSON 對網路請求來的資料進行解析或者轉為modul

SwiftyJSON 對網路請求來的資料進行解析或者轉為modul

JSON是移動端開發常用的應用層資料交換協議。最常見的場景便是,客戶端向服務端發起網路請求,服務端返回JSON文字,然後客戶端解析這個JSON文字,再把對應資料展現到頁面上。 但在程式設計的時候,處理JSON是一件麻煩事。在不引入任何輪子的情況下,我們通常需要先把JSON轉為Dictionary,然後還要記住每個資料對應的Key,用這個Key在Dictionary中取出對應的Value來使用。這個過程我們會犯各種錯誤:

* Key拼寫錯了 * 路徑寫錯了 * 型別搞錯了 * 沒拿到值懵逼了 * 某一天和服務端約定的某個欄位變更了,沒能更新所有用到它的地方 * ...

為了解決這些問題,很多處理JSON的開源庫應運而生。在Swift中,這些開源庫主要朝著兩個方向努力:

1. 保持JSON語義,直接解析JSON,但通過封裝使呼叫方式更優雅、更安全 2. 預定義Model類,將JSON反序列化為類例項,再使用這些例項

對於1,使用最廣、評價最好的庫非 SwiftyJSON 莫屬,它很能代表這個方向的核心。它本質上仍然是根據JSON結構去取值,使用起來順手、清晰。但也正因如此,這種做法沒能妥善解決上述的幾個問題,因為Key、路徑、型別仍然需要開發者去指定;

對於2,我個人覺得這是更合理的方式。由於Model類的存在,JSON的解析和使用都受到了定義的約束,只要客戶端和服務端約定好了這個Model類,客戶端定義後,在業務中使用資料時就可以享受到語法檢查、屬性預覽、屬性補全等好處,而且一旦資料定義變更,編譯器會強制所有用到的地方都改過來才能編譯通過,非常安全。這個方向上,開源庫們做的工作,主要就是把JSON文字反序列化到Model類上了。這一類JSON庫有 ObjectMapper、JSONNeverDie、HandyJSON 等。

今天我們先來看看SwiftyJSON的用法。

通常我們拿到資料會進行非常麻煩的optinonal(可選型別)進行拆包(Wrapping )操作, SwiftyJson內部會自動對optional進行拆包,大大簡化了程式碼,解析非常方便,拿到的json資料data直接扔進去

​​​​​​​

//轉成JSON物件 let jsonData = JSON.init(data)

不管需要什麼資料只要通過對jsonData進行路徑讀取 例如:

let build_name = jsonData[0]["build_name"].stringValue

值得一提的是,不需要考慮伺服器給我們返回的是什麼型別,比如去請求一個房屋的棟數"build_num": 588

我們想要獲得Int型別或者String型別都可以

let room_num1 = jsonData[0]["build_num"].stringValue
let room_num2 = jsonData[0]["build_num"].intValue
// room_num1 = 588   String型別
// room_num2 = 588   Int型別


這樣通過.stringValue.intValue就可以獲得不可選值型別,如果沒有獲取到資料的話就會返回一個預設值即.stringValue獲得空字串"".intValue得到0.arrayValue獲得空陣列[],我們就不用判斷拆包了。

當然某些場景可能需要你得到可選值型別並自己判斷是否存在,那麼我們可以通過.string .int .bool .float .array .dictionary等等方法獲取,例如:

​​​​​​​

//String if let build_name = jsonData[0]["build_name"].string {     print(build_name) } else {     //列印錯誤資訊     print(jsonData[0]["build_name"]) }

解析資料是真的非常簡單。詳細使用可以看看 或者移步 但是對於專案而言我們需要對資料進行轉換為模型,僅僅是對data進行JSON讀取是遠遠不夠的,想想,如果遇到許多地方都用到了build_name值,但是當伺服器給我們返回的欄位名字改了,我們改專案時就會顯得麻煩,甚至造成改不完全,所以我們隊資料封裝一下轉為模型,這樣修改時只用改一個地方就是model的賦值就好了。

那我的方法是:

​​​​​​​

//這是模擬資料 let baseInfo: [String : Any] = ["build_name":"置信·原墅",                                   "build_address":"學院中路與金橋路交匯處東北側",                                   "build_num": 12,                                   "room_num": 588,                                   "area_address":"浙江省溫州市鹿城區五馬街道"]

建立struct模型,當然Class也可以,但是如果不需要繼承也不復雜推薦struct(-。-不多解釋了) 並寫好建立方法

​​​​​​​

import SwiftyJSON   struct BuildBaseInfoModel {     var build_name: String?     var build_address: String?     var build_num: String?     var room_num: String?     var area_address: String?          init(jsonData: JSON) {         build_name    = jsonData["build_name"].stringValue         build_address = jsonData["build_address"].stringValue         area_address  = jsonData["area_address"].stringValue         room_num      = jsonData["room_num"].stringValue         build_num     = jsonData["build_num"].stringValue     } }

呼叫:

​​​​​​​

let jsonData = JSON(baseInfo) let model = BuildBaseInfoModel.init(jsonData: jsonData) // 會得到建立好的 BuildBaseInfoModel型別的 物件model // 也可以這樣寫 let model = BuildBaseInfoModel(jsonData: jsonData)

當然這只是普通的模型,經常會遇到複雜模型,例如:

​​​​​​​

// 面積中89是Int, 109和129是String let baseInfo: [String : Any] = ["build_name":"置信·原墅",                                   "build_address":"學院中路與金橋路交匯處東北側",                                   "area_address":"浙江省溫州市鹿城區五馬街道",                                   "area":[89,"109","129"],                                   "detail_address":["province":"浙江省",                                                     "city":"溫州市",                                                     "district":"鹿城區",                                                     "street":"五馬街道"],                                   "build_num": 12,                                   "room_num": 588]

這時候模型就應該有兩個

​​​​​​​

struct BuildBaseInfoModel {          var build_name: String?     var build_address: String?     var build_num: String?     var room_num: String?     var area_address: String?     var detail_address: DetailAddressModel     var area:[Any]?     // 這裡面積area中就不能再用arrayValue獲取了,因為arrayValue獲取的為JSON型別,我們需要轉為我們需要的物件     init(jsonData: JSON) {         build_name    = jsonData["build_name"].stringValue         build_address = jsonData["build_address"].stringValue         area_address  = jsonData["area_address"].stringValue         room_num      = jsonData["room_num"].stringValue         build_num     = jsonData["build_num"].stringValue         area          = jsonData["area"].arrayObject         detail_address = DetailAddressModel(jsonData: jsonData["detail_address"])     } } struct DetailAddressModel {     var province: String?     var city: String?     var district: String?     var street: String?          init(jsonData: JSON) {         province = jsonData["province"].stringValue         city = jsonData["city"].stringValue         district = jsonData["district"].stringValue         street = jsonData["street"].stringValue     }      }

​​​​​​​​​​​​​​let model = BuildBaseInfoModel(jsonData: jsonData)
DPrint(message: model.detail_address.city)
DPrint(message: model.area?.first)
DPrint(message: model.area?[1])
// xxxxxxxxx.swift[21], updateRoomsData(index:): Optional("溫州市")
// xxxxxxxxx.swift[22], updateRoomsData(index:): Optional(89)
// xxxxxxxxx.swift[22], updateRoomsData(index:): Optional("109")