Swift 4 JSON Decodable解码类型更改的最简单方法 [英] Swift 4 JSON Decodable simplest way to decode type change

查看:169
本文介绍了Swift 4 JSON Decodable解码类型更改的最简单方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

借助Swift 4的Codable协议,可以很好地了解幕后日期和数据转换策略.

With Swift 4's Codable protocol there's a great level of under the hood date and data conversion strategies.

给出JSON:

{
    "name": "Bob",
    "age": 25,
    "tax_rate": "4.25"
}

我想将其强制为以下结构

I want to coerce it into the following structure

struct ExampleJson: Decodable {
    var name: String
    var age: Int
    var taxRate: Float

    enum CodingKeys: String, CodingKey {
       case name, age 
       case taxRate = "tax_rate"
    }
}

日期解码策略可以将基于字符串的日期转换为日期.

The Date Decoding Strategy can convert a String based date into a Date.

是否存在基于String的Float可以做到这一点

Is there something that does that with a String based Float

否则,我会一直使用CodingKey引入String并使用计算get:

Otherwise I've been stuck with using CodingKey to bring in a String and use a computing get:

    enum CodingKeys: String, CodingKey {
       case name, age 
       case sTaxRate = "tax_rate"
    }
    var sTaxRate: String
    var taxRate: Float { return Float(sTaxRate) ?? 0.0 }

这种麻烦让我做的维护工作超出了应有的需要.

This sort of strands me doing more maintenance than it seems should be needed.

这是最简单的方法还是其他类型转换的类似于DateDecodingStrategy的东西?

Is this the simplest manner or is there something similar to DateDecodingStrategy for other type conversions?

更新:我应该注意:我也已经改写了

Update: I should note: I've also gone the route of overriding

init(from decoder:Decoder)

但这是相反的方向,因为它迫使我自己做所有这一切.

But that is in the opposite direction as it forces me to do it all for myself.

推荐答案

不幸的是,我认为当前的JSONDecoder API中不存在这样的选项.只有一个选项可以转换 exception 浮动-点值指向和来自字符串表示形式.

Unfortunately, I don't believe such an option exists in the current JSONDecoder API. There only exists an option in order to convert exceptional floating-point values to and from a string representation.

手动解码的另一种可能的解决方案是为可以对其String表示进行编码和解码的任何LosslessStringConvertible定义Codable包装器类型:

Another possible solution to decoding manually is to define a Codable wrapper type for any LosslessStringConvertible that can encode to and decode from its String representation:

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一致性:

Then you can just have a property of this type and use the auto-generated Codable conformance:

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"
    }
}

尽管不幸的是,为了与Float值交互,现在您必须谈论taxRate.decoded.

Although unfortunately, now you have to talk in terms of taxRate.decoded in order to interact with the Float value.

不过,您总是可以定义一个简单的转发计算属性以减轻这种情况:

However you could always define a simple forwarding computed property in order to alleviate this:

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"
    }
}

尽管这还没有达到应有的效果–希望JSONDecoder API的更高版本将包含更多自定义解码选项,或者能够在Codable API中表达类型转换本身.

Although this still isn't as a slick as it really should be – hopefully a later version of the JSONDecoder API will include more custom decoding options, or else have the ability to express type conversions within the Codable API itself.

但是,创建包装器类型的一个优点是,也可以使用它来简化手动解码和编码.例如,使用手动解码:

However one advantage of creating the wrapper type is that it can also be used in order to make manual decoding and encoding simpler. For example, with manual decoding:

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
    }
}

这篇关于Swift 4 JSON Decodable解码类型更改的最简单方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆