借助Swift 4的Codable协议,可以很好地了解标准日期和数据转换策略。
鉴于JSON:
{ "name": "Bob", "age": 25, "tax_rate": "4.25" }
我想将其强制为以下结构
struct ExampleJson: Decodable { var name: String var age: Int var taxRate: Float enum CodingKeys: String, CodingKey { case name, age case taxRate = "tax_rate" } }
日期解码策略可以将基于字符串的日期转换为日期。
有什么用基于String的Float做到的吗
否则,我将不得不使用CodingKey引入String并使用计算get:
enum CodingKeys: String, CodingKey { case name, age case sTaxRate = "tax_rate" } var sTaxRate: String var taxRate: Float { return Float(sTaxRate) ?? 0.0 }
这种麻烦让我做的维护工作似乎超出了需要。
这是最简单的方法还是其他类型转换与DateDecodingStrategy类似的东西?
更新 :我应该注意:我也走了重写的路线
init(from decoder:Decoder)
但这是相反的方向,因为它迫使我自己做这一切。
不幸的是,我认为当前的JSONDecoderAPI中不存在这样的选项。只有一个选项可以将 特殊的 浮点值与字符串表示形式相互转换。
JSONDecoder
手动解码的另一种可能的解决方案是为可以对其表示编码和从其表示解码的Codable任何内容定义包装器类型:LosslessStringConvertible``String
Codable
LosslessStringConvertible``String
struct StringCodableMap<Decoded : LosslessStringConvertible> : Codable { var decoded: Decoded init(_ decoded: Decoded) { self.decoded = decoded } init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let decodedString = try container.decode(String.self) guard let decoded = Decoded(decodedString) else { throw DecodingError.dataCorruptedError( in: container, debugDescription: """ The string \(decodedString) is not representable as a \(Decoded.self) """ ) } self.decoded = decoded } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(decoded.description) } }
然后,您可以拥有这种类型的属性并使用自动生成的Codable一致性:
struct Example : Codable { var name: String var age: Int var taxRate: StringCodableMap<Float> private enum CodingKeys: String, CodingKey { case name, age case taxRate = "tax_rate" } }
虽然很不幸,但是现在您必须就taxRate.decoded如何与Float价值互动进行讨论。
taxRate.decoded
Float
但是,您总是可以定义一个简单的转发计算属性以减轻这种情况:
struct Example : Codable { var name: String var age: Int private var _taxRate: StringCodableMap<Float> var taxRate: Float { get { return _taxRate.decoded } set { _taxRate.decoded = newValue } } private enum CodingKeys: String, CodingKey { case name, age case _taxRate = "tax_rate" } }
尽管这还没有达到应有的效果,但希望更高版本的JSONDecoderAPI会包含更多自定义解码选项,或者能够在CodableAPI本身内表达类型转换。
但是,创建包装器类型的一个优点是,也可以使用它来简化手动解码和编码。例如,使用手动解码:
struct Example : Decodable { var name: String var age: Int var taxRate: Float private enum CodingKeys: String, CodingKey { case name, age case taxRate = "tax_rate" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.name = try container.decode(String.self, forKey: .name) self.age = try container.decode(Int.self, forKey: .age) self.taxRate = try container.decode(StringCodableMap<Float>.self, forKey: .taxRate).decoded } }