带有Encoder和Encodable的JSON
Swift 4带来了一种更加原生的方式来编码和解码实例,并内置了对每个人最喜欢的基于文本的格式的支持:JSON!
我们不使用所有的编码和解码源代码,而是采用一种不同的方法,并逐步通过一个简单的示例:单个Int
实例如何通过JSONEncoder
变成JSON数据?
从那里,我们应该能够更进一步,了解其他原始类型,数组,字典等是如何编码的。
封存
NSCoding
作为Cocoa的一部分已经存储和检索数据很长时间了。 令人振奋的消息是,由于NSKeyedArchiver
已有15年的历史了,Apple终于宣布弃用NSArchiver
。 😜
一个好主意是,如果可以对诸如字符串和数字之类的单个实例进行编码和解码 ,则可以存档和取消存档整个对象图。
编码所有事物
在Swift标准库中,除了编码 器之外,还有一些可编码的东西。
- 可编码是一种协议。 符合类型可以将自身编码为其他表示形式。
- 编码器也是一种协议。 编码器负责将可
Encodable
内容转换为其他格式,例如JSON或XML。
Encodable
类似于NSCoding
但作为Swift协议,您的Swift结构和枚举也可以加入聚会。 类似地,尽管Encoder
还是协议而不是抽象类,但它与NSCoder
对应。
一个简单的整数
您不能使用JSONEncoder
编码裸标量,而是需要顶级数组或字典。 为简单起见,让我们从编码包含单个整数[42]
的数组开始。
let encoder = JSONEncoder()
let jsonData = try! encoder.encode([42])
首先,我们实例化JSONEncoder
,然后使用数组在其上调用encode()
。 那里发生了什么事?
// JSONEncoder.swift
open func encode(_ value: T) throws -> Data {
let encoder = _JSONEncoder(options: self.options)
encode()
方法采用一些Encodable
值,并返回原始JSON Data
。
实际的编码工作在私有类_JSONEncoder
。 此方法将JSONEncoder
保留为具有友好公共接口的类型,并将_JSONEncoder
为实现Encoder
协议的fileprivate
(每个人的最爱!)类。
// continued from above
try value.encode(to: encoder)
注意相反的情况:在原始呼叫站点,我们要求编码器对值进行编码; 在此,编码器要求该值将其自身编码到专用编码器。
可编码
让我们退后一步,在这里查看协议的相关部分。
首先是可Encodable
-请记住,这是用于整数和数组等可编码值的协议。
public protocol Encodable {
func encode(to encoder: Encoder) throws
}
我们将对包装数组[42]
并只考虑整数值42
以使事情变得简单。 Int
符合Encodable
,我们可以看一下它的encode(to:)
方法的作用:
extension Int : Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self)
}
}
我们要求编码器提供一个容器,然后要求该容器编码self
,即整数值。
编码器
我们的下一个侧边栏是讨论Encoder
-这是_JSONEncoder
这样的类的协议,这些协议将可编码的值转换为某种一致的格式非常繁重。
public protocol Encoder {
// [...]
func container(keyedBy type: Key.Type) -> KeyedEncodingContainer
func unkeyedContainer() -> UnkeyedEncodingContainer
func singleValueContainer() -> SingleValueEncodingContainer
}
归功于上述三种访问器方法,编码器可以处理三种值:
- 键控容器(词典)
- 未加密的容器(数组)
- 单值容器(用于标量值)
返回代码以对Int
进行编码:
// extension Int : Codable
var container = encoder.singleValueContainer()
try container.encode(self)
首先,从JSON编码器获取一个单值容器,其代码如下:
// _JSONEncoder
func singleValueContainer() -> SingleValueEncodingContainer {
return self
}
好吧,这很简单: _JSONEncoder
本身符合SingleValueEncodingContainer
并返回自身。 让我们快速浏览一下该协议:
public protocol SingleValueEncodingContainer {
// [...]
mutating func encode(_ value: Int) throws
}
对于各种简单类型,例如Bool
, String
等,还有许多其他的encode
方法。但是我们感兴趣的是Int
:
// extension _JSONEncoder : SingleValueEncodingContainer
func encode(_ value: Int) throws {
assertCanEncodeNewValue()
self.storage.push(container: box(value))
}
这是关键时刻! 有某种存储方式,我们正在将盒装价值推到上面。
但是存储空间是多少? 盒子里有什么? 🤔
存储
要了解存储,让我们跳到最后。 我们在这里陷入困境,但是您还记得我们是如何开始的吗? 这是JSONEncoder
中的encode()
方法,我们在数组中传递了一个整数。 该方法的最后一行如下所示:
// JSONEncoder.swift
return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)
因此,这一切的全部要点是拥有一个顶级容器(数组或字典),该容器将传递给JSONSerialization
,以前称为NSJSONSerialization
。
该存储是另一个文件fileprivate
结构,该结构保留一堆容器。 当心,Objective-C将在这里开始展示自己:
fileprivate struct _JSONEncodingStorage {
/// The container stack.
/// Elements may be any one of the JSON types
/// (NSNull, NSNumber, NSString, NSArray, NSDictionary).
private(set) var containers: [NSObject] = []
}
这就解释了盒子。 我们的数组[42]
将变成一个NSMutableArray
,上面的box()
调用会将整数变成一个NSNumber
,该NSNumber
被添加到该数组中:
// extension _JSONEncoder
fileprivate func box(_ value: Int) -> NSObject {
return NSNumber(value: value)
}
事实证明,如果您做得足够深入,那就是Objective-C。
总结一下: JSONEncoder
和朋友将我们的Swift值转换为它们的Foundation对象等效JSONEncoder
,然后通过JSONEncoder
将这些对象转换为JSON。
结束括号
这是要求JSONEncoder
用单个整数[42]
编码数组的最后一组步骤:
- 数组,将自己编码为
_JSONEncoder
- 数组设置非密钥容器(
NSMutableArray
存储) - 数组遍历所有元素并对它们进行编码
一种。 整数,将自己编码为_JSONEncoder
b。 整数设置单值容器
C。 整数要求容器自行编码
- 容器将整数放入
NSNumber
,添加到数组中
7.使用JSONSerialization
对顶级NSMutableArray
进行编码
8.利润! 💰
您可以在标准库源的JSONEncoder.swift和Codable.swift中找到所有相关代码。
现在相反,解码呢? 以及如何编写自定义编码器和解码器以用于除JSON之外的其他格式(例如协议缓冲区)? 请继续关注更多信息,或者为什么不深入研究代码并查看发现的内容呢?
}