Swift 4 Codable协议 - 基本用法

时间:2022-06-01 13:00:44

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实现,以完成更高级的转换。