本文为 Raywenderlich 出版的《Expert Swift》第 8 章节《Codable》阅读笔记。
Swift 4.0 就引入了 Codable
协议。话不多说,进入正题:
示例 JSON:
1 | { |
如何 Decode?
即如何解析这段 JSON?
维持原有层级结构
这个 JSON 对应的 Swift 模型为:
1 | import Foundation |
Customer1
结构体模型的层级结构与 JSON 对象的层级结构保持一致。这样就可以利用 JSONDecoder
把示例 JSON 成功地解析为 Customer1
结构体模型。
自定义 JSON 对应模型的层级结构
以上述 Customer1
模型为例,如果想访问该顾客所在的城市:
1 | let jack = Customer1() |
如果想直接通过 jack.city
来访问 jack 所在的城市信息,该怎么处理?
1. 使用计算属性
1 | var city: String { |
但这种方式的弊端是:万一如果想把 AddressInfo
这嵌套的一层直接去掉,得手写好多类似上面的代码。
2. 使用嵌套的 CodingKey 枚举
使用嵌套的 CodingKey 枚举可以把具有层级的 JSON “拍扁”。
1 | enum CodingKeys: String, CodingKey { |
还得自己实现对应的 decode
方法:
1 | init(from decoder: Decoder) throws { |
在自己实现 decode
方法时,还需要对几类容器(Container)有了解:
- Keyed Container:最常见的容器之一,可用来解析键值对数据,对应的 key 定义在
CodingKeys
枚举中。 - Unkeyed Container:用来解析不是以字符串为 key 的结构,例如常见的数组结构。
- Single Value Container:用来把单一的数据解析为有具体类型的数据结构。
- Nested Container:当某个容器成为另一个容器的子容器时,它被称为嵌套容器
我们可以方便地把容器理解为:一个对数据编解码的环境。
被“拍扁”后的模型为:
1 | struct Customer2: Encodable { |
如何 Encode?
示例模型对象:
1 | import Foundation |
我们可以利用 Encodable
的默认实现来把模型对象转为 JSON 对象:
1 | let dateFormatter: DateFormatter = { |
修改 JSON 对象的层级结构
如果想修改 encode 之后的 JSON 对象的层级结构呢?还是得使用嵌套的 CodingKey
枚举,并自己实现 encode
方法:
1 | func encode(to encoder: Encoder) throws { |
达成 Codable
通过上述自定义 encode 和 decode 操作之后,就达成 Codable 。
1 | import Foundation |
可以看出:自定义实现 encode 和 decode 方法所带来的代码量还是不小的,然而于此同时也带来了较高的灵活度。
举个例子,假如 JSON 对象中 zip
对应的值的类型是字符串,如“87119”。而 Customer
模型中 zip
变量类型为 Int
,则可以在自定义 decode 方法中特别处理:
1 | // 当 JSON 对象中 `zip` key 对应的 value 类型是 String 时的兼容处理 |
再者,如果 JSON 新加一个 memberType
字段来表示顾客的身份:
1 | enum MemberType: Int { |
突然某一天,收到的 JSON 对象中 memberType
为 3,意味超级 vip 客户,这是业务迭代新增的需求,而在 Customer
未对此改动作相应的更新时,也在 decode 方法里对这种情况作兼容处理,比如直接设为 .unknow
。
JSONEncoder
- KeyEncodingStrategy:内置默认的
convertToSnakeCase
策略,如模型中的变量名为:xmlContents
,经过convertToSnakeCase
策略之后,对应的 JSON key 会变为xml_contents
。也提供自定义的接口,让开发者自行决定。 - OutputFormatting:内置三种默认的输出格式化方式
- prettyPrinted:iOS 7 开始支持
- sortedKeys:iOS 11 开始支持
- withoutEscapingSlashes:iOS 13 开始支持
- userInfo:外部可通过此与自定义的 encode 过程进行信息传递。
- DateEncodingStrategy:内置
iso8601
等编码格式,同时也提供传入自定义的DateFormatter
对象对日期进行自定义处理。 - DataEncodingStrategy:内置
base64
等编码格式,同时也提供自定义接口,让开发者自行决定。
JSONDecoder 的结构与 JSONEncoder 很相似,不再一一赘述。