- 最開始的時候仍是使用
NSJSONSerialization
轉成字典和數組來使用!- 後來蘋果用
Swift
從新實現了JSONSerialization
能夠避免用NSArray
和NSDictionary
來橋接,提升解析效率。- 隨後不少三方
JSON
庫相繼出現,例如:SwiftyJSON
、HandyJSON
......等,請原諒我一直沒有用過這些三方庫,雖然有參考學習過,但我一直維護改進本身封裝的JSON庫,這期間雖然使用JSON
的簡便性有所提升,但我仍然以爲很麻煩。- 從
Swift 4.0
開始,蘋果提供了Codable
協議和JSONDecoder
,JSONEncoder
,這一改進使得一衆三方庫黯然失色,將JSON
到模型的便捷方式提高到了一個新的高度,但這種方式仍是過於強硬,模型類型過於死板,稍有不慎就會由於類型不符致使解析失敗,所以我將本身的JSON
庫添加了Codable
協議支持,做爲官方庫的一個有益補充。- 在
Swift 4.2
的時候dynamicMemberLookup
(動態屬性)的加入又給(軟)JSON
模型加了一劑強心針。
{"name":"小王","age":5,"reads":["格林童話","安徒生童話"]}git
之前的三方JSON
庫在將相對動態的json數據解析到模型的時候除了要寫屬性類型外,在構造方法中,也要寫上對應的key,模型寫起來很囉嗦,例如:github
struct User {
var name:String
var age:Int
var reads:[String]
init(_ json:JSON) {
name = json["name"].string
age = json["age"].int
reads = json["reads"].array
}
}
複製代碼
屬性越多,構造方法中寫的也越多,相同的鍵字符串和變量名顯得十分囉嗦json
自從蘋果革命性的增長了Codable
協議,如今只須要寫模型的屬性,而沒必要寫構造方法swift
struct User: Codable {
var name:String
var age:Int
var reads:[String]
}
複製代碼
解析的時候也十分簡便後端
let user = try! JSONDecoder().decode(User.self, from: data)
複製代碼
多是由於蘋果做爲大公司,有着極度嚴苛的編碼規範和先後端協做標準,所以JSONDecoder
對類型的要求是十分嚴苛的,若是換成下面這段JSON
內容就會解析失敗:數組
{"name":"小王","age":"5","reads":["格林童話","安徒生童話"]}服務器
這其中將age
的類型變成了字符串的"5"
與模型的Int類型不符,使得整個JSON都沒法解析。實際的開發中先後端未必有那麼嚴苛的規範,也不必定總能碰到靠譜的後端,所以常常由於各類類型不符緣由解析模型失敗就很蛋疼。網絡
好比說通常狀況下後端給咱們返回的JSON都有幾個固定字段判斷返回結果可用性,例如:post
{
"errorCode":0,
"errorMessage":"成功",
"result":{}
}
複製代碼
{
"errorCode":1,
"errorMessage":"缺乏參數",
"result":null
}
複製代碼
在上面的例子中errorCode
是服務器返回的錯誤狀態0
表示請求成功errorMessage
是錯誤狀態提示文本,result
根據不一樣的API接口,返回不一樣格式的JSON
內容。 若是想寫一個模型的話:學習
struct ResponseJSON: Codable {
var errorMessage: String
var errorCode: Int
var result: ???????
}
複製代碼
這就尷尬了,result
屬性的類型無法填,由於沒法肯定此刻它該使用什麼模型。 解決方法以下:
- 不使用模型,改用字典,但這樣就回歸原始,用起來不方便。
result
屬性類型使用[String:Any]
,雖然當下使用了模型,但後面使用很不方便。ResponseJSON
使用泛型定義由調用網絡請求接口預傳入result
所需的模型- 定義一種動態類型保存未徹底解析的
JSON
,允許對應接口使用result
字段時懶解析到恰當的模型
其中,比較好的方法是3
和4
, 但方法3仍是有缺陷,相同的API返回的JSON
結構就必定相同麼?可能有一個type
屬性表示着另外一個屬性是什麼結構。這種時候,預傳入泛型就無能爲力了。
所以咱們須要定義一個符合Codable
協議的動態類型
來保存未徹底解析的JSON
, 在須要的時候能夠懶解析成所需模型。
public enum JSON: Codable {
case object (Object)
case array (Array)
case string (String)
case number (Number)
case bool (Bool)
case null
case error (Error, ignore:[String])
}
複製代碼
代碼固然不止這點,詳細內容能夠參考(Basic.frameworks)中關於JSON的部分
ResponseJSON
定義成:struct ResponseJSON: Codable {
var errorMessage: String
var errorCode: Int
var result: JSON
}
複製代碼
在須要的時候使用
let user = try! JSON.Decoder().decode(User.self, from: responseJSON.result)
複製代碼
或者其餘模型
JSON
)中object
、array
、string
、number
、bool
、null
這6個是咱們熟知的json數據類型,而error
是什麼呢?爲何要添加它?顧名思義,error
就是錯誤,添加一個錯誤類型是用來代替屬性獲取可選鏈。
error
,咱們的JSON
應該是這樣使用的。var json:JSON = .....
let name = json.result?.list?[0].name?.string
複製代碼
這種方式是利用可選鏈?
來傳遞屬性,若是其中一個屬性不存在,不會報錯,而是獲得一個nil
值,通常狀況這樣已經足夠,但若是出現錯誤的json,想要排查問題就須要逐層查看或測試,錯誤信息在可選鏈的第幾層徹底丟失。
如今加入了error (Error, ignore:[String])
類型,就無需使用可選鏈來傳遞屬性了
var json:JSON = .....
let name = json.result.list[0].name.string
複製代碼
當某個屬性沒法獲取時,能夠給Error類型,並且還能夠傳遞下去,而且不會丟失錯誤信息。最終轉換string或者int時,能夠根據開發環境和生產環境,選擇中斷或者返回默認值!
JSONDecoder
是蘋果官方提供的將二進制JSON解析成模型Model
的解析類JSON.Decoder
是(Basic.frameworks)中仿照官方方法,將二進制或JSON軟模型解析成模型Model
的解析類
- 他們的做用類似,只是
JSON.Decoder
削弱了數據類型的強制要求,JSON.Decoder
提供了比官方JSONDecoder
更多的解析策略,JSON.Decoder
爲了方便提供了全局策略默認值的修改。
Data
或String
能夠經過官方的JSONDecoder
或(Basic.frameworks)中的JSON.Decoder
解析到實現Codable
協議的Model
模型Data
或String
能夠經過(Basic.frameworks)中的JSON.Decoder
或解析到JSON
軟模型、Model
模型或二者混合JSON
軟模型能夠經過(Basic.frameworks)中的JSON.Decoder
解析到Model
模型或混合模型。Model
模型或混合模型能夠經過(Basic.frameworks)中的JSON.Encoder
系列化成JSON
軟模型,或Data
二進制Model
模型或混合模型能夠經過官方的JSONEncoder
系列化成Data
二進制JSON
軟模型能夠經過官方的JSONEncoder
或(Basic.frameworks)中的JSON.Encoder
系列化成Data
二進制
參見文章《純Swift項目-HTTP(Basic.frameworks)》一文中的例子