Swift 4新增了Codable协议,它是一个组合协议,包含Encodable和Decodable两个协议。代码定义如下:
/// A type that can convert itself into and out of an external representation.
public typealias Codable = Decodable & Encodable
/// A type that can encode itself to an external representation.
public protocol Encodable {
public func encode(to encoder: Encoder) throws
}
/// A type that can decode itself from an external representation.
public protocol Decodable {
public init(from decoder: Decoder) throws
}
Encodable协议定义了encode(to encoder: )函数,它用于编码我们的数据。
Decodable协议定义了init(from decoder: Decoder )函数,用于解析数据。
Codable协议最常用的场景就是JSON数据和模型的转换。
基本用法
Codable有一个默认的实现,在很多情况下使用默认实现就可以很简便完成JSON数据与模型的转换。
如以下示例
JSON结构:
{
"name": "张三",
"age": 10
}
对象模型:
struct Student: Codable {
var name: String
var age: Int
}
解码
我们使用JSONDecoder只需要很少的代码就可以把JSON数据解析为Student对象。
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let student = try! decoder.decode(Student.self, for: jsonData)
注意:示例里JSON数据的键和Student结构体的属性名是相同的。
编码
相反,我们也可以使用JSONEncoder来把Student对象编码为JSON数据。
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(student)
print(String(data: data, encoding: .utf8)!)
其中encoder.outputFormatting = .prettyPrinted是为了让JSON数据输出的结构更可读。
输出如下:
{
"name": "张三",
"age": 10
}
适配键
在上面的例子里,JSON数据的键和Student结构体的属性名是相同的。如果JSON的键与结构体属性不一致,我们可以使用CodingKey来把两者关联起来,适配JSON的键。
JSON
{
"name": "张三",
"age": 10,
"residential_address":"广东省深圳市南山区xxx"
}
Student
struct Student: Codable {
var name: String
var age: Int
var residentialAddress: String
enum CodingKeys: String, CodeingKey {
case name
case age
case residentialAddress = "residential_address"
}
}
在此例子里,residential_address为新增的键,而Student结构体里的residentialAddress为驼峰式命名。所以我们需要对redidentital_address和residentialAddress做关联。
关联代码定义在CodingKeys枚举里。
数组
let decoder = JSONDecoder()
let students = try decoder.decode([Student].self, from: data)
使用Codable协议,处理数组也很简单,只要Array<T>里的T是继承于Codable协议的,那么Array<T>也是可以根据Codable协议解编码的。
嵌套
对于包含嵌套的结构体,只要嵌套的结构体是继承于Codable协议,那么整个结构体也是可以根据Codable协议解编码。
如下面例子里JSON新增了scores属性用于存放每一科目对应的分数。
JSON
{
"name": "张三",
"age": 10,
"scores":[
{
"subject": "Chinese",
"score": 100
},
{
"subject": "Math",
"score": 100
},
{
"subject": "English",
"score": 100
}
]
}
SubjectScore结构体
struct SubjectScore: Codable {
var subject: String
var score: Int
}
Student结构体
struct Student: Codable {
var name: String
var age: Int
var scores: [SubjectScore]
}
这个示例用SubjectScore结构体表示JSON里scores数组里单一科目的分数。它继承与Codable,Student也继承与Codable,所以Student也是可解析的。
总结
这些例子里我们都是使用了Codable的默认实现来解码/编码来做JSON数据与模型之间的转换。与Swift3 转换JSON数据不同,使用Codable的默认实现不需要我们手动解析JSON数据。
当然,如果我们也可以编写自己的Codable实现,以完成更高级的转换。