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
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")