swift 序列化Codable

文章目录
  1. 1. 前言
  2. 2. Swift 中的序列化
    1. 2.1. 没有Encoder和Decoder
    2. 2.2. 使用编码器,类型遵循 Codable(Encodable & Decodable) 协议
  3. 3. JSONEncoder/JSONDecoder
    1. 3.1. 不需要手动解码
    2. 3.2. 需要手动解码
      1. 3.2.1. json中的key值与属性名称不一致
    3. 3.3. 类型嵌套

前言

序列化是将对象的状态信息转换为可以存储或传输的形式的过程(对象<–>I/O流)。
对象信息序列化以后变成 I/O 流:

  1. 可以本地化存储(持久化对象)
  2. 网络通讯(网络传输对象)
  3. 定制协议,跨平台、跨语言通讯

Swift 中的序列化

  1. Swift 4.0 之前仍需要手动解析
  2. Swift 4.0 以后,提供 Codable 协议
  3. 仍然存在问题

没有Encoder和Decoder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let json: [String : Any] = [
"name": "cap",
"points": 1,
"description": "this is a cat",
]
struct Product {
var name: String
var points: Int
var description: String?

// 需要手动解析
init?(json: [String: Any]) {
guard let name = json["name"] as? String,
let points = json["points"] as? Int,
let description = json["description"] as? String else { return nil }
self.name = name
self.points = points
self.description = description
}
}
if let p = Product(json: json) {
print(p.name + " \(p.points) " + p.description!)
}

使用编码器,类型遵循 Codable(Encodable & Decodable) 协议

  1. 类型遵循Codable
  2. Encode
  3. Decode

2017年6月发布的 Swift4.0 中的 Codable(Encodable & Decodable) 协议,表明该协议具有被序列化和/或反序列化的能⼒。
Swift 标准库中的所有基本类型都遵循 Codable 协议,Data,Date, URL,CGPoint 和 CGRect 在内的许多 Apple 框架中的常⽤数据类型,也已经适配了 Codable。
自定义类型需要用户遵循 Codable协议。

1
2
3
4
5
6
7
8
9
10
public typealias Codable = Decodable & Encodable

/// 某个类型可以将⾃身编码为⼀种外部表示
public protocol Encodable { /// 将值编码到给定的 encoder 中
public func encode(to encoder: Encoder) throws
}
/// 某个类型可以从外部表示中解码得到⾃身
public protocol Decodable { /// 通过从给定的 decoder 中解码来创建新的实例
public init(from decoder: Decoder) throws
}

⼀旦你拥有 codable 类型的值,你可以创建⼀个编码器,并让它将这个值转换到像是 JSON 这样的序列化格式。反过来,⼀个解码器可以将序列化后的数据转回为它原来类型的⼀个实例。

Swift ⾃带两套编码解码器,JSONEncoder/JSONDecoderPropertyListEncoder/PropertyListDecoder,它们存在于 Foundation 中。

JSONEncoder/JSONDecoder

不需要手动解码

属性名 = key,属性类型 = value 类型一致,使用 decoder 对json解码,不需要手动解码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
let json = """
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange",
"points": 100
}
]
""".data(using: .utf8)!

struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}

// 解码
let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

print("The following products are available:")
for product in products {
print("\t\(product.name) (\(product.points) points)")
if let description = product.description {
print("\t\t\(description)")
}
}

// 与上文无关,只描述下编码过程
let jsonEncoder = JSONEncoder()
let jsonData = try? jsonEncoder.encode(products)
let str = String(decoding: jsonData!, as: UTF8.self)
print(str)
let jsonObject = try JSONSerialization.jsonObject(with: jsonData!, options: .allowFragments)
print(jsonObject)

GroceryProduct 类中属性都遵循 Codable 协议,Array也是,所以不需要实现 encode(to encoder: Encoder)init(from decoder: Decoder) 方法

需要手动解码

json中的key值与属性名称不一致

但是这个类型实际上并不⼀定需要是枚 举)。提供⾃定义的编码键是⼀种很简单,⽽且是声明式的改变类型编码的⽅式。在枚举中,我 们可以:

→ 使⽤明确给定的字符串值,在编码后的输出中重命名字段,或者

→ 将某个键从枚举中移除,以此完全跳过字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
let json = """
[
{
"product_name": "Bananas",
"product_cost": 200,
"description": "A banana grown in Ecuador."
},
{
"product_name": "Oranges",
"product_cost": 100,
"description": "A juicy orange."
}
]
""".data(using: .utf8)!

struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?

private enum CodingKeys: String, CodingKey {
case name = "product_name"
case points = "product_cost"
case description
}
}

let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

print("The following products are available:")
for product in products {
print("\t\(product.name) (\(product.points) points)")
if let description = product.description {
print("\t\t\(description)")
}
}

类型嵌套