使用Swift 4编码和解码JSON

Apple上周在圣何塞举行的WWDC 2017主题演讲和国情咨文中宣布了Xcode 9以及Swift4。使它成为Swift 4标准库的最重要的变化之一就是一系列编码,解码和解码协议。类型实例的序列化,允许用户与JSON以及与本地磁盘进行相互转换。 这些协议(最著名的是CodableEncodableDecodable旨在作为Swift的本机答案,以解决开发人员在Swift 3或更早版本中序列化对象时遇到的一些限制。

这些协议仅在一个星期前就已经流行了,但是Apple可以在线获取一些出色的文档。 我花了一些时间对这些API进行试验,以进一步了解它们的工作原理,可能的功能以及在生产代码库中使用它们可能遇到的一些优点和缺点。

到目前为止,Swift社区中最大的开放问题之一是“您使用哪个第三方框架对JSON反序列化? 还是你自己滚?”。 自从我第一次开始编写Swift以来,我已经尝试了几种第三方框架并开发了自己的框架。 我经常对此感到沮丧的是,每个框架都带来了自己独特的方法,但是它们似乎都误选了可选内容,缺乏强大的错误处理能力,并且使用了不直观的自定义运算符。 Swift 4的Codable协议现在为解决该问题提供了一种通用且推荐的方法,因此我根据一些我经常会遇到的关键用例进行了实验。

反序列化

假设您有一个结构Product并且想从网络请求中收到的JSON响应中反序列化实例。 确保Product和任何自定义属性类型均符合协议Codable (如果不序列化回JSON,则仅Decodable )。

在要将Data对象反序列化为Product模型的那一点上,初始化JSONDecoder对象,然后调用decoder.decode(_: from:) 。 如果解码操作失败,则此函数将引发错误,因此您可能需要将其包装在do,try catch语句中。

就是这样。 假设Product模型上的所有属性名称与您要反序列化的JSON结构中的字段名称完全对应,Swift将处理初始化模型实例所需的所有工作。

序列化

Product序列化回JSON仅需要模型及其所有自定义属性类型符合协议Codable (如果您不从JSON反序列化,则仅Encodable )。

在您想要将Product实例序列化为要在网络请求或其他任何地方发送的Data ,请初始化JSONEncoder对象,然后调用encode(_:) 。 如果编码操作失败,此函数也会引发错误,请随意将其包装起来,然后尝试再次捕获。

嵌套类型

在Swift 4中,对具有同样也是Codable类型的属性的Product模型进行反序列化变得非常容易。非常简单,只需确保每个嵌套类型都符合Codable (或者在需要时仅Encodable / Decodable )。 就像您期望的那样, JSONDecoder将以与解码顶级对象相同的方式来处理嵌套属性的解码。

自定义属性名称

当然,使用JSON从未如此简单。 作为移动工程师,我们经常无法控制我们正在使用的Network API,并且想要为要从JSON有效负载解码的属性定义自定义字段名称。

重要的是要了解,默认情况下,Swift会自动使用您定义为字段名称的属性名称来从JSON进行解码。 在Codable类型上为属性定义自定义字段名称就像在对象CodingKeys上定义一个枚举一样简单,该对象的rawValue类型为String并符合CodingKey协议。 您需要为模型上的每个属性定义一个案例。 然后,将每种情况的RawValue用作要从JSON解码的JSON字段名称。

自定义键路径

在我尝试使用更复杂的键路径和JSON结构进行的简短介绍中,似乎Swift的处理这些方法需要一些习惯,并且涉及很多样板。

您可能会遇到的常见结构是嵌套在顶级对象中的对象属性,在这种情况下,该字段称为“ product”。 重要的是要了解,Swift的这些嵌套键路径的方法是使用“容器”的概念。 JSON结构中的每个对象级别都被视为层次结构中的容器,而Swift JSONDecoder将基于各个容器进行解码。

由于您的Swift Codable类型具有平坦的属性列表,因此默认情况下, JSONDecoder只会尝试从JSON有效负载中的顶级Container解码您的属性。 为了实现其他目的,您需要编写自己的public init(from decoder:) throws函数的实现,并手动拆分包含您的属性的nestedContainers。

在上面的示例中,定义了一个单独的CodingKey枚举,其中包含顶级容器产品的字段名称。 在Decodable初始化程序中,使用此键对顶级容器的值进行反序列化,然后对嵌套容器中的每个后续嵌套值进行一次反序列化。

显然,Swift对复杂键路径和JSON结构的处理依赖于许多样板,并且需要回落到像我们以前那样手动解码每个属性。 但是从许多方面来看,此缺点鼓励开发人员与API团队紧密合作,以尽可能定义更简单的JSON结构。

错误处理

许多第三方JSON反序列化/序列化框架的最大缺点之一是缺乏彻底的错误处理功能。 对象可能导致反序列化过程失败的原因有很多,原因很多,例如缺少JSON字段,错误的类型或自定义转换失败。

如果没有任何错误处理的实现,当出现问题时,调试生产应用程序中的问题将变得异常困难。 因此,要弄清这些问题的根本原因,就需要安装调试控制台,设置无数个断点以及逐步执行异步代码以找出哪些字段未发送或发送错误。

当解码/编码失败时,Swift的JSONEncoderJSONDecoder对象会引发错误,并且这些错误向开发人员提供了明确的具体错误反馈,以JSONDecoder开发人员出了什么问题。