编码与对象映射器
我最近一直在尝试使用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
迁移到我们的所有新功能中。 我敢肯定,我错过了这个功能,但是我选择了最重要的功能。