Codable 是swift4的新特性, 使用其能夠用更少的代碼更快的實現數據編解碼json
一種基於文本的用於表示信息的格式,人類和計算機都很容易讀寫這種格式,也是目前使用最普遍的標準swift
簡單寫一個json文本數組
let json = """
{
"name" : "xiaoming",
"age" : 18,
"score" : 99.5
}
""".data(using: .utf8)!
複製代碼
咱們用上面的json來模擬網絡數據請求獲得的結果,下面構建model和解析bash
咱們建立一種適合這種json表示的swift類型 也就是model, 讓json中的值和swift系統中定義類型互相匹配。網絡
String
的 Dictionary
相似struct Student : Codable {
var name : String
var age : Int
var score : Double
}
複製代碼
咱們首先定義一個結構體 Student
來對應數據中的頂層對象, 這裏咱們使用結構體,使用 Class
也無妨。函數
上面的結構體 Student
遵循了 Codable
協議, 其大大提高了對象和其表示之間相互轉換的體驗。ui
理解 Codable 最好的方式就是看它的定義:編碼
typealias Codable = Decodable & Encodable
spa
Codable
是一種 混合類型,由 Decodable
和 Encodable
協議構成。code
Decodable
協議定義了一個初始化函數:
init(from decoder: Decoder) throws
聽從 Decodable
協議的類型可使用任何 Decoder 對象進行初始化。
Encodable
協議定義了一個方法:
func encode(to encoder: Encoder) throws
任何 Encoder
對象均可以建立聽從了 Encodable 協議類型的表示。
只有當一個類型知足這個協議的全部要求的時候,咱們才能說這個類型 聽從 那個協議了。 對於 Decodable
而言,惟一的要求就是 init(from:)
初始化方法。
init(from:)
初始化方法接受一個 Decoder
參數。 Decoder 協議須要闡明將 Decodable 對象的表示解碼成對象的要求。爲了適應各類數據交換格式,解碼器和編碼器都使用名爲 容器(container)的抽象。容器是用來存儲值的,能夠存儲一個值也能夠存儲多個值,能夠像字典同樣有鍵去對應值,也能夠像數組同樣不須要鍵。...
由於上面那個 JSON 表示的頂層有一個對象, 因此咱們就建立一個有鍵的容器,而後用不一樣的鍵來解碼各個屬性。
咱們建立一個 CodingKeys
枚舉,定義屬性名稱和容器的鍵之間的映射。這個枚舉聲明其原始類型是 String
,同時聲明使用 CodingKey 協議。由於每一個名字都和 JSON 中的鍵相同,因此咱們不用爲這個枚舉提供明確的原始值。
struct Student: Decodable {
// ...
private enum CodingKeys: String, CodingKey {
case name
case age
case score
}
}...
複製代碼
接下來,在 init(from:)
初始化函數裏咱們建立一個鍵控容器,調用decoder
的 container(keyedBy:)
方法,並傳入 CodingKeys
做爲參數。
最後,咱們調用 container
的 decode(_:,forKey:)
方法把每一個屬性初始化一下。
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.score = try container.decode(Double.self, forKey: .score)
}...
複製代碼
咱們有了一個 Student
模型,也聽從了 Decodable
協議,如今咱們能夠從 JSON 表示中建立一個 student
對象了
先引用 Foundation
,以便使用 JSONDecoder
和 JSONEncoder
。
import Foundation
建立一個 JSONDecoder
對象並調用其 decode(_:from:)
方法
let decoder = JSONDecoder()
let student = try! decoder.decode(Student.self, from: json)
複製代碼
試驗轉換成功與否
print(student.name)
print(student.age)
print(student.score)
複製代碼
下面咱們實現 Encodable
協議要求的 encode(to:)
方法。 encode(to:)
和 init(from:)
就像一對鏡像同樣工做。 先調用 encoder
的 container(keyedBy:)
方法建立一個 container
, 和上一步同樣,傳入一個 CodingKeys.self
參數。 這裏咱們把 container
看成一個變量(使用 var
而不是 let
), 由於這個方法作的是填充 encoder
的參數,須要進行修改。咱們對每一個屬性調用一次 encode(_:forKey:)
並傳入屬性的值和它對應的鍵。...
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.name, forKey: .name)
try container.encode(self.age, forKey: .age)
try container.encode(self.score, forKey: .score)
}...
複製代碼
let encoder = JSONEncoder()
let reencodedJSON = try! encoder.encode(stuent)
print(String(data: reencodedJSON, encoding: .utf8)!)...
複製代碼
JSON 對空格和空行(whitespace)不敏感,可是你可能會在乎。若是你以爲輸出不美觀,把 JSONEncoder
的 outputFormatting
屬性設置爲 .prettyPrinted
就行了
把全部咱們寫的 Codable
的實現都刪掉。這樣,應該就只剩下結構和屬性的定義了。
struct Student : Codable {
var name : String
var age : Int
var score : Double
}
複製代碼
直接運行代碼,試試編碼和解碼 JSON。有什麼變化嗎?徹底沒有。 這讓咱們看到了 Codable 的殺手級特性:
Swift 自動整合了 Decodable
和 Encodable
的一致性。
一種類型只要在其聲明中採用協議便可,也就是說,不用在擴展中再作額外的事,只要它的每一個屬性都有一個符合其協議,其餘一切都自動完成了。
JSON頂層是數組的狀況:
let jsonModels = """ [ { "name" : "xiaoming", "age" : 18, "score" : 99.5 }, { "name" : "daxiong", "age" : 19, "score" : 66 } ] """.data(using: .utf8)!
複製代碼
把它解析成 Student 對象的數組十分簡單:像以前同樣調用 decode(_:from:)
,把 Student.self
換成 [Student].self
以便去解析 Student
數組而不是單個對象。
let students = try! decoder.decode([Student].self, from: jsonModels)
爲何這麼簡單就能夠了?這要歸功於 Swift 4 的另外一個新特性:條件一致性。
// swift/stdlib/public/core/Codable.swift.gyb
extension Array : Decodable where Element : Decodable {
// ...
}
複製代碼
咱們再在數組的外層套一個字典的形式:
let jsonComplex = """ { "students" : [ { "name" : "xiaoming", "age" : 18, "score" : 99.5 }, { "name" : "daxiong", "age" : 19, "score" : 66 } ] } """.data(using: .utf8)!
複製代碼
若是 Array
和Dictionary
包含的 KeyType
和 ValueType
都聽從 Decodable
協議,那麼這樣的數組或字典自己也就聽從了 Decodable
協議:
// swift/stdlib/public/core/Codable.swift.gyb
extension Dictionary : Decodable where Key : Decodable,
Value : Decodable {
// ...
}...
複製代碼
所以你能夠把以前 decode(_:from:)
的調用參數直接換成 [String: [Student]].self
(Dictionary<String, Plane>
的簡寫):
let jsonData = try! decoder.decode([String: [Student]].self, from: jsonComplex)
let students = jsonData["students"]
複製代碼
或者你也能夠建立一個聽從 Decodable
協議的新類型,而後給這個類型一個屬性來放 [Student] ,該屬性的名字應和 JSON 中的鍵名相對應,再把這個新類型傳入 decode(_:from:)
函數中:
struct Brige: Decodable {
var students: [Student]
}
let Brige = try! decoder.decode(Brige.self, from: json)
let students = Brige.students...
複製代碼
Codable
是一種 混合類型,由 Decodable
和 Encodable
協議構成。Decodable
和 Encodable
。Array
或 Dictionary
中的每個元素都聽從 Codable
類型,那麼它們自己也會聽從 Codable