iOS 学习使用 Swift Codable

Codable 是swift4的新特性, 使用其能够用更少的代码更快的实现数据编解码json

JSON

一种基于文本的用于表示信息的格式,人类和计算机都很容易读写这种格式,也是目前使用最普遍的标准swift

简单写一个json文本数组

let json = """
{
    "name" : "xiaoming",
    "age" : 18,
    "score" : 99.5

}
""".data(using: .utf8)!
复制代码

咱们用上面的json来模拟网络数据请求获得的结果,下面构建model和解析bash

构建模型

咱们建立一种适合这种json表示的swift类型 也就是model, 让json中的值和swift系统中定义类型互相匹配。网络

  • JSON中的对象是无序键值对,和swift中包含 StringDictionary 相似
  • JSON 将数字表示成一系列数,不受任何语义的影响,它不区分整数和浮点数,不区分定长和非定长数字,也不区分是二进制仍是十进制。每一个实现自行决定如何解释这些数字。
  • 对于具备 Optional 属性的类型, Codable 能够自动将 null 映射为 nil。
struct Student : Codable {
    var name : String
    var age : Int
    var score : Double
}
复制代码

咱们首先定义一个结构体 Student 来对应数据中的顶层对象, 这里咱们使用结构体,使用 Class 也无妨。函数

Codable 协议简介

上面的结构体 Student 遵循了 Codable 协议, 其大大提高了对象和其表示之间相互转换的体验。ui

理解 Codable 最好的方式就是看它的定义:编码

typealias Codable = Decodable & Encodablespa

Codable 是一种 混合类型,由 DecodableEncodable 协议构成。code

Decodable 协议定义了一个初始化函数:

init(from decoder: Decoder) throws

听从 Decodable 协议的类型可使用任何 Decoder 对象进行初始化。

Encodable 协议定义了一个方法:

func encode(to encoder: Encoder) throws

任何 Encoder 对象均可以建立听从了 Encodable 协议类型的表示。

只有当一个类型知足这个协议的全部要求的时候,咱们才能说这个类型 听从 那个协议了。 对于 Decodable 而言,惟一的要求就是 init(from:) 初始化方法。

init(from:) 初始化方法接受一个 Decoder 参数。 Decoder 协议须要阐明将 Decodable 对象的表示解码成对象的要求。为了适应各类数据交换格式,解码器和编码器都使用名为 容器(container)的抽象。容器是用来存储值的,能够存储一个值也能够存储多个值,能够像字典同样有键去对应值,也能够像数组同样不须要键。...

由于上面那个 JSON 表示的顶层有一个对象, 因此咱们就建立一个有键的容器,而后用不一样的键来解码各个属性。

咱们建立一个 CodingKeys 枚举,定义属性名称和容器的键之间的映射。这个枚举声明其原始类型是 String,同时声明使用 CodingKey 协议。由于每一个名字都和 JSON 中的键相同,因此咱们不用为这个枚举提供明确的原始值。

struct Student: Decodable {
    // ...

    private enum CodingKeys: String, CodingKey {
        case name
        case age
        case score
    }
}...
复制代码

接下来,在 init(from:) 初始化函数里咱们建立一个键控容器,调用decodercontainer(keyedBy:) 方法,并传入 CodingKeys 做为参数。

最后,咱们调用 containerdecode(_:,forKey:) 方法把每一个属性初始化一下。

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.name = try container.decode(String.self, forKey: .name)
    self.age = try container.decode(Int.self, forKey: .age)
    self.score = try container.decode(Double.self, forKey: .score)
}...
复制代码

咱们有了一个 Student 模型,也听从了 Decodable 协议,如今咱们能够从 JSON 表示中建立一个 student 对象了

把 JSON 解码成模型对象

先引用 Foundation,以便使用 JSONDecoderJSONEncoder

import Foundation

建立一个 JSONDecoder 对象并调用其 decode(_:from:) 方法

let decoder = JSONDecoder()
let student = try! decoder.decode(Student.self, from: json)
复制代码

试验转换成功与否

print(student.name) 
print(student.age) 
print(student.score) 
复制代码

将模型对象编码为 JSON

下面咱们实现 Encodable 协议要求的 encode(to:) 方法。 encode(to:)init(from:) 就像一对镜像同样工做。 先调用 encodercontainer(keyedBy:) 方法建立一个 container, 和上一步同样,传入一个 CodingKeys.self 参数。 这里咱们把 container 看成一个变量(使用 var 而不是 let), 由于这个方法作的是填充 encoder 的参数,须要进行修改。咱们对每一个属性调用一次 encode(_:forKey:) 并传入属性的值和它对应的键。...

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.name, forKey: .name)
    try container.encode(self.age, forKey: .age)
    try container.encode(self.score, forKey: .score)
}...

复制代码
let encoder = JSONEncoder()
let reencodedJSON = try! encoder.encode(stuent)

print(String(data: reencodedJSON, encoding: .utf8)!)...

复制代码

JSON 对空格和空行(whitespace)不敏感,可是你可能会在乎。若是你以为输出不美观,把 JSONEncoderoutputFormatting 属性设置为 .prettyPrinted 就行了

删除无用的代码

把全部咱们写的 Codable 的实现都删掉。这样,应该就只剩下结构和属性的定义了。

struct Student : Codable {
    var name : String
    var age : Int
    var score : Double
}
复制代码

直接运行代码,试试编码和解码 JSON。有什么变化吗?彻底没有。 这让咱们看到了 Codable 的杀手级特性:

Swift 自动整合了 DecodableEncodable 的一致性。

一种类型只要在其声明中采用协议便可,也就是说,不用在扩展中再作额外的事,只要它的每一个属性都有一个符合其协议,其余一切都自动完成了。

尝试解析各类格式的JSON文本

JSON顶层是数组的状况:

let jsonModels = """ [ { "name" : "xiaoming", "age" : 18, "score" : 99.5 }, { "name" : "daxiong", "age" : 19, "score" : 66 } ] """.data(using: .utf8)!
复制代码

把它解析成 Student 对象的数组十分简单:像以前同样调用 decode(_:from:),把 Student.self 换成 [Student].self 以便去解析 Student 数组而不是单个对象。

let students = try! decoder.decode([Student].self, from: jsonModels)

为何这么简单就能够了?这要归功于 Swift 4 的另外一个新特性:条件一致性。

// swift/stdlib/public/core/Codable.swift.gyb
extension Array : Decodable where Element : Decodable {
    // ...
}

复制代码

咱们再在数组的外层套一个字典的形式:

let jsonComplex = """ { "students" : [ { "name" : "xiaoming", "age" : 18, "score" : 99.5 }, { "name" : "daxiong", "age" : 19, "score" : 66 } ] } """.data(using: .utf8)!

复制代码

若是 ArrayDictionary 包含的 KeyTypeValueType 都听从 Decodable 协议,那么这样的数组或字典自己也就听从了 Decodable 协议:

// swift/stdlib/public/core/Codable.swift.gyb
extension Dictionary : Decodable where Key : Decodable, 
                                       Value : Decodable {
    // ...
}...
复制代码

所以你能够把以前 decode(_:from:) 的调用参数直接换成 [String: [Student]].self (Dictionary<String, Plane> 的简写):

let jsonData = try! decoder.decode([String: [Student]].self, from: jsonComplex)
        let students = jsonData["students"]
复制代码

或者你也能够建立一个听从 Decodable 协议的新类型,而后给这个类型一个属性来放 [Student] ,该属性的名字应和 JSON 中的键名相对应,再把这个新类型传入 decode(_:from:) 函数中:

struct Brige: Decodable {
    var students: [Student]
}

let Brige = try! decoder.decode(Brige.self, from: json)
let students = Brige.students...
复制代码
  • Codable 是一种 混合类型,由 DecodableEncodable 协议构成。
  • 只要一种类型中的每一个属性都听从协议,在类型定义时声明一下,Swift 就会自动整合 DecodableEncodable
  • 若是 ArrayDictionary 中的每个元素都听从 Codable 类型,那么它们自己也会听从 Codable
相关文章
相关标签/搜索