编程语言SwiftSwift 进阶【六】编码和解码
轩辕十四将程序内部的数据结构序列化为一些可交换的数据格式,以及反过来将通用的数据格式反序列化为内部使用的数据结构,这在编程中是一项非常常见的任务。Swift 将这些操作称为编码(encoding)和解码(decoing)。Swift 4 的一个主要特性就是定义了一套标准的编码和解码数据的方法,所有的自定义类型都能选择使用这套方法。
概览
Codable 系统(以其基本“协议”命名,而这个协议其实是一个类型别名)的设计主要围绕三个核心目标;
- 普遍性 — 它对结构体,枚举和类都适用。
- 类型安全 — 像是 JSON 这样的可交换格式通常都是弱类型,而你的代码应该要使用强类型数据。
- 减少模板代码 — 在让自定义类型加入这套系统时,应该让开发者尽可能少地写重复的“适配代码”。编译器应该为你自动生成这些代码。
某个类型通过声明自己遵守 Encodable 和/或 Decodable 协议来表明自己具备被序列化和/或反序列化的能力。这两个协议各自只有一个必须实现的方法 - Encodable 定义了 encode(to:)
用来对值自身进行编码,Decodable 指定了一个初始化方法,来从序列化的数据中创建实例:
1 2 3 4 5 6 7 8 9 10
| public protocol Encodable { public func encode(to encoder: Encoder) throws }
public protocol Decodable { public init(from decoder: Decoder) throws }
|
因为大多数实现了其中一个协议的类型,也会实现另一个,所以标准库中还提供了 Codable 类型别名,来作为这两个协议组合后的简写:
1
| public typealias Codable = Decodable & Encodable
|
标准库中包括 Bool
,数值类型和 String
等所有基本类型,都直接是 Codable 类型。那些含有 Codable 元素的可选值,数组,字典和集合,也都满足 Codable。最后,包括 Data
,Date
,URL
,CGPoint
和 CGRect
在内的许多 Apple 框架中的常用数据类型,也已经适配了 Codable。
Encoding
因为 JSON 是最常见的格式,所以我们来集中研究一下 JSONEncoder
和 JSONDecoder
。
我们先来建两个结构体,一个是封装了坐标的结构体,一个是封装了地点的结构体。
1 2 3 4 5 6 7 8 9 10
| struct Coordinate: Codable { var latitude: Double var longitude: Double }
struct Placemark: Codable { var name: String var coordinate: Coordinate }
|
接下来我们可以将一个 Placemark
数组编码为 JSON 格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let places = [ Placemark(name: "Berlin", coordinate: Coordinate(latitude: 52, longitude: 13)), Placemark(name: "Cape Town", coordinate: Coordinate(latitude: -34, longitude: 18)) ]
do { let encoder = JSONEncoder() let jsonData = try encoder.encode(places) let jsonString = String(decoding: jsonData, as: UTF8.self)
} catch { print(error.localizedDescription) }
|
Decoding
这一次我们来看一个复杂一点的,但是在实际应用中,能够经常见到的 JSON 格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| let jsonStr = """ { "success": true, "message": "got the locations!", "data": { "LocationList": [ { "LocID": 1, "LocName": "Downtown" }, { "LocID": 2, "LocName": "Uptown" }, { "LocID": 3, "LocName": "Midtown" } ] } } """
|
这里有三层结构,第一层是最外面的一层,第二层是 data
的这一层,第三层是 LocationList
对应的这一层,这一层是一个数组。
下面我们来建立三个结构体,分别对应着三层结构:
第一层 Location
1 2 3 4 5
| struct Location: Codable { var success: Bool var message: String var data: LocationData }
|
第二层 LocationData
1 2 3
| struct LocationData: Codable { var LocationList: [LocationItem] }
|
第三层 LocationItem
1 2 3 4
| struct LocationItem: Codable { var LocID: Int var LocName: String }
|
JSON 解码
1 2 3 4 5 6 7 8 9
| let jsonDecode = JSONDecoder() if let jsonData = jsonStr.data(using: .utf8) { do { let decoded = try jsonDecode.decode(Location.self, from: jsonData) print(decoded.data.LocationList.first?.LocName ?? "") } catch let error { print(error.localizedDescription) } }
|
一种更容易看出 JSON 包含情况的结构体声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct Location: Codable { struct LocationData: Codable { struct LocationItem: Codable { var LocID: Int var LocName: String } var LocationList: [LocationItem] } var success: Bool var message: String var data: LocationData }
|