编码与对象映射器

我最近一直在尝试使用Swift的新Codable协议,以将从远程服务获取的JSON映射到Swift模型对象。

出于某种背景,在Swift 4中添加了Codable,作为一种干净地允许对象将自身转换为外部表示的方法。 可编码本身只是可解码和可编码的一种类型。

在这篇文章中,我将重点介绍Decodable部分,因为它是我感兴趣的远程JSON表示形式的转换。

比较方式

过去我曾经广泛使用过ObjectMapper,但由于Codable现在已内置到Swift中,所以我想对两者进行比较。 我要比较的功能是:

  • 验证🕵️
  • 自定义转换(映射到自定义类型,例如将JSON字符串映射到正则表达式)
  • 错误处理⚠️

我要解析的数据是我们为BBC Sport应用远程获取的真实配置

这是一个相当简单的JSON结构,但是它有几个正则表达式,我想将其映射到NSRegularExpression 而不是String

对象映射器

使用ObjectMapper映射此JSON模型所需的结构如下。

这些结构都符合ImmutableMappable 协议,这意味着他们需要一个构造函数,该构造函数接受Map对象,如果映射失败,则抛出错误。

验证方式

要执行验证,您可以使用Optionals。 在此示例中,我们确定该应用程序可以在没有这些电子邮件地址的情况下运行,因此这些电子邮件是可选的。 如果不存在键或无法将其map.values("emails")转换为正确的类型,则map.values("emails")会引发错误。 我们利用try? 捕获该错误,如果有错误,只需将其转换为nil值即可。

如果我们确定特定属性是必需的,那么我们就不会将其标记为可选属性,并允许错误传播。

总体而言,使用ImmutableMappable验证非常简单

您可能已经注意到,在此调用map.value(“regex”, using: RegexTransformer())了另外一个参数map.value(“regex”, using: RegexTransformer())这是用于将String转换为NSRegularExpression的自定义转换,这使我的NSRegularExpression

自定义转换

ObjectMapper开箱即用地支持自定义转换,这非常简单。

在这里,我们只是实现TransformType协议和关联的transformFromJSON方法。 那需要一个类型,然后我们将其强制转换为String ,然后安全地try?String转换为NSRegularExpression

然后可以在任何地方重用此变压器reuse

错误处理

为了测试错误处理,我将使用缺少output键的JSON文件。

通过ObjectMapper运行此命令时,我们会收到一条非常有用的错误消息。

 映射时出错。 
-原因:无法转换为“字符串”
-位置:Config.init(map :):30
-键:输出
-currentValue:无

它告诉我们快速找到问题所在的一切。 我发现使用AlamofireObjectMapper集成时,错误被抑制了,这不理想。


可编码

开箱即用的等效Codable实现如下

尽管使用CodingKey协议将键定义为enums但它与ObjectMapper非常相似。

使用Codable的一个非常好的功能是,如果枚举Codable等于该属性,则可以为您生成初始化程序,而我们要映射的类型本身就是CodableConfig 。上面的示例是CodableConfig 。 由于所有属性本身都是可Decodable我们无需编写初始化程序! 🙌🏻

验证方式

这与ObjectMapper完全相同

如果存在错误映射,则初始化程序将引发错误,可以在呼叫站点进行处理。 同样,在这里充分利用Optionals来决定如何最好地处理错误。

自定义转换

您会在上面的代码中注意到,当我们尝试映射到我们的NSRegularExpression类型时,一切都变得不太清楚。 我们突然必须实现初始化程序init(from decoder: Decoder)并从Decoder获得一个KeyedDecodingContainer

如果您想了解更多细节,已经对此进行了很好的撰写,我在这里不再赘述。

现在编写代码变得非常冗长,我们正在重复转换代码。 在这种情况下,这只是多余的一行,但是在很多情况下,我想编写更想隔离的更复杂的转换,就像使用ObjectMapper

因此,我做了一个小库来添加对自定义转换的支持🎉这可通过CocoaPods获得,或者您可以直接复制源代码,因为它只有几个文件。

您可能会考虑在您不拥有的类型上添加扩展名,但是很好地解释了为什么在Swift Evolution上无法做到这一点。

使用CodableExtensions库,我们现在可以简化代码,使其看起来与ObjectMapper非常相似

RegexCodableTransformer也与我们之前使用ObjectMapper非常相似

该库还简化了container.decode()的接口,因此在推断类型时不再需要传递该类型。

错误处理

我正在使用相同的JSON文件来比较以前的错误处理。 错误看起来像

  keyNotFound(config_spike.CodableRewriter。(_ 4D474241C6D85B5C48988D77CA644850中的CodingKeys)。输出,Swift.DecodingError.Context(codingPath:[config_spike.CodableConfig。(_ 4D474241C6D85B5C48988DOutput中的CodingKeys与相关的Description]的输出(De_DescriptionError)无关) “)。”,underlyingError:nil)) 

该错误看上去并不像💅🏻,但它具有调试该问题的所有功能。

结论

两种方法之间的相似之处远胜于差异。 如果转换很重要,则ObjectMapper可以直接使用。 但是,进行转换的主要动机之一就是向Apple创建的标准转移,而不必引入另一个库。

如果转换很重要,则通过添加几个协议,您可以使用Codable获得相同的行为

我们已经决定现在将Codable迁移到我们的所有新功能中。 我敢肯定,我错过了这个功能,但是我选择了最重要的功能。