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