純Swift項目-JSON(Basic.frameworks)

Swift JSON 發展史

  1. 最開始的時候仍是使用NSJSONSerialization轉成字典和數組來使用!
  2. 後來蘋果用Swift從新實現了JSONSerialization能夠避免用NSArrayNSDictionary來橋接,提升解析效率。
  3. 隨後不少三方JSON庫相繼出現,例如:SwiftyJSONHandyJSON......等,請原諒我一直沒有用過這些三方庫,雖然有參考學習過,但我一直維護改進本身封裝的JSON庫,這期間雖然使用JSON的簡便性有所提升,但我仍然以爲很麻煩。
  4. Swift 4.0 開始,蘋果提供了Codable協議和JSONDecoderJSONEncoder,這一改進使得一衆三方庫黯然失色,將JSON到模型的便捷方式提高到了一個新的高度,但這種方式仍是過於強硬,模型類型過於死板,稍有不慎就會由於類型不符致使解析失敗,所以我將本身的JSON庫添加了Codable協議支持,做爲官方庫的一個有益補充。
  5. Swift 4.2 的時候dynamicMemberLookup(動態屬性)的加入又給(軟)JSON模型加了一劑強心針。

例子JSON

{"name":"小王","age":5,"reads":["格林童話","安徒生童話"]}git

之前的三方JSON庫的缺陷

之前的三方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


JSONDecoder 的缺陷

自從蘋果革命性的增長了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屬性的類型無法填,由於沒法肯定此刻它該使用什麼模型。 解決方法以下:

  1. 不使用模型,改用字典,但這樣就回歸原始,用起來不方便。
  2. result屬性類型使用[String:Any],雖然當下使用了模型,但後面使用很不方便。
  3. ResponseJSON使用泛型定義由調用網絡請求接口預傳入result所需的模型
  4. 定義一種動態類型保存未徹底解析的JSON,允許對應接口使用result字段時懶解析到恰當的模型

其中,比較好的方法是34, 但方法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)中objectarraystringnumberboolnull這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.Decoder 的區別

  • JSONDecoder 是蘋果官方提供的將二進制JSON解析成模型Model的解析類
  • JSON.Decoder是(Basic.frameworks)中仿照官方方法,將二進制或JSON軟模型解析成模型Model的解析類

JSON解析關係圖
如圖所示,不一樣線條樣式表明不一樣的數據流向來源

  1. 他們的做用類似,只是JSON.Decoder削弱了數據類型的強制要求,
  2. JSON.Decoder提供了比官方JSONDecoder更多的解析策略,
  3. JSON.Decoder爲了方便提供了全局策略默認值的修改。

流向總結

  1. DataString能夠經過官方的JSONDecoder或(Basic.frameworks)中的JSON.Decoder解析到實現Codable協議的Model模型
  2. DataString能夠經過(Basic.frameworks)中的JSON.Decoder或解析到JSON軟模型、Model模型或二者混合
  3. JSON軟模型能夠經過(Basic.frameworks)中的JSON.Decoder解析到Model模型或混合模型。
  4. Model模型或混合模型能夠經過(Basic.frameworks)中的JSON.Encoder系列化成JSON軟模型,或Data二進制
  5. Model模型或混合模型能夠經過官方的JSONEncoder系列化成Data二進制
  6. JSON軟模型能夠經過官方的JSONEncoder或(Basic.frameworks)中的JSON.Encoder系列化成Data二進制

實際使用實例

參見文章《純Swift項目-HTTP(Basic.frameworks)》一文中的例子

相關文章
相關標籤/搜索