Xcode 9, iOS 11 ,Swift 4出现JSONEncoder
和JSONDecoder
来实现JSON格式的编解码
JSONEncoder
An object that encodes instances of a data type as JSON objects.
编码数据为JSON对象
简单使用
义定义杂货食品结构体,并遵守Codable
协议
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
Codable
协议为可编解码协议的类型别名
public typealias Codable = Decodable & Encodable
// 编码协议
public protocol Encodable {
/// Encodes this value into the given encoder.
///
/// If the value fails to encode anything, `encoder` will encode an empty
/// keyed container in its place.
///
/// This function throws an error if any values are invalid for the given
/// encoder's format.
///
/// - Parameter encoder: The encoder to write data to.
func encode(to encoder: Encoder) throws
}
// 解码协议
public protocol Decodable {
/// Creates a new instance by decoding from the given decoder.
///
/// This initializer throws an error if reading from the decoder fails, or
/// if the data read is corrupted or otherwise invalid.
///
/// - Parameter decoder: The decoder to read data from.
init(from decoder: Decoder) throws
}
编码例子
func jsonEncoder() {
let pear = GroceryProduct(name: "Pear",
points: 250,
description: "A ripe pear")
// 实例化JSONEncoder
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted // 输出格式
// 编码pear
let data = try! encoder.encode(pear)
// 打印编码之后的数据
print(String(data: data, encoding: .utf8)!)
}
运行输出
{
"name" : "Pear",
"points" : 250,
"description" : "A ripe pear"
}
也可以设置输出格式为sortedKeys
,对keys进行排序
encoder.outputFormatting = .sortedKeys
运行输出
{"description":"A ripe pear","name":"Pear","points":250}
JSONDecoder
An object that decodes instances of a data type from JSON objects.
将JSON对象解码成对应的实例数据
简单使用
func jsonDecoder() {
// 定义jsonData
let jsonData = """
{
"name": "Durian",
"points": 600,
"description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
// 实例化JSONDecoder
let decoder = JSONDecoder()
// 解码jsonData
let product = try! decoder.decode(GroceryProduct.self, from: jsonData)
// 获取实例属性
print("name: \(product.name), points: \(product.points), des: \(product.description!)")
}
运行输出
name: Durian, points: 600, des: A fruit with a distinctive scent.
一些使用场景
初始化内容
struct Toy: Codable {
var name: String
}
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy
}
// 创建Json数据
let toy = Toy(name: "Teddy Bear")
let employee = Employee(name: "John Appleseed", id: 7, favoriteToy: toy)
// 实例化JSONEncoder和JSONDecoder
let encoder = JSONEncoder()
let decoder = JSONDecoder()
- Encoding and Decoding Nested Types
Employee包含Toy属性,属于嵌套类型(nested type)
let data = try! encoder.encode(employee)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameEmployee = try! decoder.decode(Employee.self, from: data)
print(sameEmployee)
运行输出
{
"name":"John Appleseed",
"id":7,
"favoriteToy":{"name":"Teddy Bear"}
}
Employee(name: "John Appleseed", id: 7, favoriteToy: DataSaveProject.Toy(name: "Teddy Bear"))
// DataSaveProject工程名
- Switching Between Snake Case and Camel Case Formats
camel case 表示驼峰形式(如:looksLikeThis)
snake case 表示下划线形式(如:looks_like_this_instead)
// key编码策略
encoder.keyEncodingStrategy = .convertToSnakeCase
let snakeData = try! encoder.encode(employee)
let snakeString = String(data: snakeData, encoding: .utf8)!
// key解码策略
decoder.keyDecodingStrategy = .convertFromSnakeCase
let camelEmployee = try! decoder.decode(Employee.self, from: snakeData)
运行输出
{
"name":"John Appleseed",
"id":7,
"favorite_toy": {"name":"Teddy Bear"}} // 单词之间使用下划线 _
Employee(name: "John Appleseed", id: 7, favoriteToy: DataSaveProject.Toy(name: "Teddy Bear"))
- Working With Custom JSON Keys
使用自定义key,这里使用gift替换favoriteToy
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy
// 1、定义特殊枚举CodingKeys,指定值为String,遵守CodingKey协议
// 2、将favoriteToy设置gift
enum CodingKeys: String, CodingKey {
case name, id, favoriteToy = "gift"
}
}
编解码实现同上
let data = try! encoder.encode(employee)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameEmployee = try! decoder.decode(Employee.self, from: data)
print(sameEmployee)
运行输出
{
"name":"John Appleseed",
"id":7,
"gift":{"name":"Teddy Bear"}}
Employee(name: "John Appleseed", id: 7, favoriteToy: DataSaveProject.Toy(name: "Teddy Bear"))
- Working With Flat JSON Hierarchies
去除嵌套类型,实现如下数据结构
{
"name" : "John Appleseed",
"id" : 7,
"gift" : "Teddy Bear"
}
修改Employee
结构体,单独实现编解码协议
struct Employee: Encodable {
var name: String
var id: Int
var favoriteToy: Toy
// 1、定义编码key
enum CodingKeys: CodingKey {
case name, id, gift
}
// 2、实现编码协议
func encode(to encoder: Encoder) throws {
// 3、创建key编码容器,可理解为字典
var container = encoder.container(keyedBy: CodingKeys.self)
// 4、使用容器container编码name, id, gift属性
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
try container.encode(favoriteToy.name, forKey: .gift)
}
}
// 实现解码协议
extension Employee: Decodable {
init(from decoder: Decoder) throws {
// 获取key编码容器
let container = try decoder.container(keyedBy: CodingKeys.self)
// 获取name, id, gift等属性值
name = try container.decode(String.self, forKey: .name)
id = try container.decode(Int.self, forKey: .id)
let gift = try container.decode(String.self, forKey: .gift)
favoriteToy = Toy(name: gift)
}
}
初始化内容同上
let data = try! encoder.encode(employee)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameEmployee = try! decoder.decode(Employee.self, from: data)
print(sameEmployee)
- Working With Deep JSON Hierarchies
实现身层级JSON数据
{
"name" : "John Appleseed",
"id" : 7,
"gift" : {
"toy" : {
"name" : "Teddy Bear"
}
}
}
可以看到name属性在toy之内,toy属性在gift之内,为了实现该数据结构,需要为gift属性使用嵌套key容器(nested keyed containers)
struct Employee: Encodable {
var name: String
var id: Int
var favoriteToy: Toy
// 1、定义编码key
enum CodingKeys: CodingKey {
case name, id, gift
}
enum GiftKeys: CodingKey {
case toy
}
// 2、实现编码协议
func encode(to encoder: Encoder) throws {
// 3、创建key编码容器,可理解为字典
var container = encoder.container(keyedBy: CodingKeys.self)
// 4、使用容器container编码name, id, gift属性
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
//5、获取嵌套容器
var giftContainer = container.nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
try giftContainer.encode(favoriteToy, forKey: .toy)
}
}
// 实现解码协议
extension Employee: Decodable {
init(from decoder: Decoder) throws {
// 获取key编码容器
let container = try decoder.container(keyedBy: CodingKeys.self)
// 获取name, id, gift等属性值
name = try container.decode(String.self, forKey: .name)
id = try container.decode(Int.self, forKey: .id)
// 获取嵌套容器
let giftContainer = try container.nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
favoriteToy = try giftContainer.decode(Toy.self, forKey: .toy)
}
}
运行上面测试内容输出
{
"name":"John Appleseed",
"id":7,
"gift":{
"toy":{"name":"Teddy Bear"}
}
}
Employee(name: "John Appleseed", id: 7, favoriteToy: DataSaveProject.Toy(name: "Teddy Bear"))
- Encoding and Decoding Dates
实现日期的编解码
struct Toy: Codable {
var name: String
}
struct Employee: Codable {
var name: String
var id: Int
var birthday: Date
var favoriteToy: Toy
}
extension DateFormatter {
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
return formatter
}()
}
简单使用
// 创建数据
let toy = Toy(name: "Teddy Bear")
let employee = Employee(name: "John Appleseed",
id: 7,
birthday: Date(),
favoriteToy: toy)
// 实例化JSONEncoder和JSONDecoder
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// 设置日期编解码策略
encoder.dateEncodingStrategy = .formatted(.dateFormatter)
decoder.dateDecodingStrategy = .formatted(.dateFormatter)
let data = try! encoder.encode(employee)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameEmployee = try! decoder.decode(Employee.self, from: data)
print(sameEmployee)
运行输出
{
"id":7,
"favoriteToy":{"name":"Teddy Bear"},
"name":"John Appleseed",
"birthday":"22-10-2019"
}
Employee(name: "John Appleseed",
id: 7,
birthday: 2019-10-21 16:00:00 +0000,
favoriteToy: DataSaveProject.Toy(name: "Teddy Bear"))
- Encoding and Decoding Subclasses
实现编解码子类
struct Toy: Codable {
var name: String
}
// 父类
class BasicEmployee: Codable {
var name: String
var id: Int
init(name: String, id: Int) {
self.name = name
self.id = id
}
}
// 子类
class GiftEmployee: BasicEmployee {
var birthday: Date
var toy: Toy
enum CodingKeys: CodingKey {
case employee, birthday, toy
}
init(name: String, id: Int, birthday: Date, toy: Toy) {
self.birthday = birthday
self.toy = toy
super.init(name: name, id: id)
}
// 编码
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(birthday, forKey: .birthday)
try container.encode(toy, forKey: .toy)
let baseEncoder = container.superEncoder(forKey: .employee)
try super.encode(to: baseEncoder)
}
// 解码
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
birthday = try container.decode(Date.self, forKey: .birthday)
toy = try container.decode(Toy.self, forKey: .toy)
let baseDecoder = try container.superDecoder(forKey: .employee)
try super.init(from: baseDecoder)
}
}
extension DateFormatter {
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
return formatter
}()
}
简单使用
// 创建数据
let toy = Toy(name: "Teddy Bear")
// 实例化JSONEncoder和JSONDecoder
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// 设置日期编解码策略
encoder.dateEncodingStrategy = .formatted(.dateFormatter)
decoder.dateDecodingStrategy = .formatted(.dateFormatter)
let giftEmployee = GiftEmployee(name: "John Appleseed",
id: 7,
birthday: Date(),
toy: toy)
let data = try! encoder.encode(giftEmployee)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameEmployee = try! decoder.decode(GiftEmployee.self, from: data)
print(sameEmployee.name)
运行输出
{
"toy": { "name":"Teddy Bear"},
"employee": {"name":"John Appleseed","id":7},
"birthday":"22-10-2019"
}
John Appleseed
- Handling Arrays With Mixed Types
复杂类型的编解码
struct Toy: Codable {
var name: String
}
enum AnyEmployee:Encodable {
case defaultEmployee(String, Int)
case customEmployee(String, Int, Date, Toy)
case noEmployee
// 编码key
enum CodingKeys: CodingKey {
case name, id, birthday, toy
}
func encode(to encoder: Encoder) throws {
// 根据编码key获取编码容器
var container = encoder.container(keyedBy: CodingKeys.self)
// 根据类型进行数据处理
switch self {
case .defaultEmployee(let name, let id):
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
case .customEmployee(let name, let id, let birthday, let toy):
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
try container.encode(birthday, forKey: .birthday)
try container.encode(toy, forKey: .toy)
case .noEmployee:
let context = EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Invalid employee!")
throw EncodingError.invalidValue(self, context)
}
}
}
extension AnyEmployee: Decodable {
init(from decoder: Decoder) throws {
// 1 获取container
let container = try decoder.container(keyedBy: CodingKeys.self)
let containerKeys = Set(container.allKeys)
let defaultKeys = Set([.name, .id])
let customKeys = Set([.name, .id, .birthday, .toy])
// 2 对Keys进行匹配
switch containerKeys {
case defaultKeys:
// 获取属性值初始化实例
let name = try container.decode(String.self, forKey: .name)
let id = try container.decode(Int.self, forKey: .id)
self = .defaultEmployee(name, id)
case customKeys:
let name = try container.decode(String.self, forKey: .name)
let id = try container.decode(Int.self, forKey: .id)
let birthday = try container.decode(Date.self, forKey: .birthday)
let toy = try container.decode(Toy.self, forKey: .toy)
self = .customEmployee(name, id, birthday, toy)
default:
self = .noEmployee
}
}
}
简单使用
let toy = Toy(name: "Teddy Bear")
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// 设置日期编解码策略
encoder.dateEncodingStrategy = .formatted(.dateFormatter)
decoder.dateDecodingStrategy = .formatted(.dateFormatter)
// 创建数据源
let employees = [AnyEmployee.defaultEmployee("John Appleseed", 7),
AnyEmployee.customEmployee("Jack", 22, Date(), toy)]
let employeesData = try! encoder.encode(employees)
let employeesString = String(data: employeesData, encoding: .utf8)!
print(employeesString)
let sameEmployees = try! decoder.decode([AnyEmployee].self, from: employeesData)
print(sameEmployees)
运行输出
[
{"name":"John Appleseed","id":7},
{"id":22,"name":"Jack","birthday":"22-10-2019","toy":{"name":"Teddy Bear"}}
]
[
AnyEmployee.defaultEmployee("John Appleseed", 7),
AnyEmployee.customEmployee("Jack", 22, 2019-10-21 16:00:00 +0000,
Toy(name: "Teddy Bear"))
]
- Working With Arrays
数组的编解码
struct Toy: Codable {
var name: String
}
struct Label: Encodable {
var toy: Toy
func encode(to encoder: Encoder) throws {
// 因为没有设置key,所以使用unkeyedContainer,对于数组是不需要key的
var container = encoder.unkeyedContainer()
try container.encode(toy.name.lowercased())
try container.encode(toy.name.uppercased())
try container.encode(toy.name)
}
}
extension Label: Decodable {
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var name = ""
while !container.isAtEnd {
name = try container.decode(String.self)
}
toy = Toy(name: name)
}
}
简单使用
let toy = Toy(name: "Teddy Bear")
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// 创建label
let label = Label(toy: toy)
let labelData = try! encoder.encode(label)
let labelString = String(data: labelData, encoding: .utf8)!
print(labelString)
let sameLabel = try! decoder.decode(Label.self, from: labelData)
print(sameLabel)
运行输出
["teddy bear","TEDDY BEAR","Teddy Bear"]
Label(toy: Toy(name: "Teddy Bear"))
- Working With Arrays Within Objects
对象数据结构中拥有数组的编解码
struct Toy: Encodable {
var name: String
var label: String
enum CodingKeys: CodingKey {
case name, label
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
// 嵌套无key容器
var labelContainer = container.nestedUnkeyedContainer(forKey: .label)
// 编码数组数据
try labelContainer.encode(label.lowercased())
try labelContainer.encode(label.uppercased())
try labelContainer.encode(label)
}
}
extension Toy: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
var labelContainer = try container.nestedUnkeyedContainer(forKey: .label)
var labelName = ""
while !labelContainer.isAtEnd { // 遍历获取数组最后一个值
labelName = try labelContainer.decode(String.self)
}
label = labelName
}
}
简单使用
let encoder = JSONEncoder()
let decoder = JSONDecoder()
let toy = Toy(name: "Teddy Bear", label: "Teddy Bear")
let data = try! encoder.encode(toy)
let string = String(data: data, encoding: .utf8)!
print(string)
let sameToy = try! decoder.decode(Toy.self, from: data)
print(sameToy)
运行输出
{
"name":"Teddy Bear",
"label":["teddy bear","TEDDY BEAR","Teddy Bear"]
}
Toy(name: "Teddy Bear", label: "Teddy Bear")
参考
JSONEncoder
JSONDecoder
Encoding-and-decoding-in-swift