JSONDecoder的基元类型上的自定义初始化器

时间:2019-12-04 20:49:46

标签: swift jsondecoder

如何为primitive类型(如Int,Bool)自定义JSONDecoder的行为?

问题出在这里

  • 类型不能依赖后端。例如:布尔值可以为true / false或“ true” /“ false”(布尔值可以用双引号引起来)

  • 我们至少有300个具有平均15个属性的Codable结构,并且编写解码逻辑很麻烦。逻辑也差不多,因此代码变得重复了

因此,我正在寻找一种解决方案,以使如果存在Type mismatch基本类型应该能够处理它,如果没有,则应将其设置为nil,前提是该类型是Optional。 / p>


我为此尝试了多种方法

1。在所有原始类型上都使用Wrapper并处理解码逻辑。以下是Bool上的包装器示例

struct Bool1: Codable{
    private var bool: Bool?

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let b = try? container.decode(Bool.self) {
            self.bool = b
        } else if let string = try? container.decode(String.self) {
            if string.lowercased() == "true" {
                self.bool = true
            } else if string.lowercased() == "false" {
                self.bool = false
            } else {
                throw Error()
            }
        }
    }
}

但这在其他开发人员之间造成了不必要的混乱,因为包装类型不如原生类型自然出现。另外,无法直接访问该值(始终需要xyz.bool)来提取原始值

2。创建一个从Decodable和子类JSONDecoder

继承的新协议
protocol KKDecodable: Decodable {
    init(decoder1: Decoder)
}

extension Bool: KKDecodable {
    init(decoder1: Decoder) {
     // Logic for creating Bool from different types
    }
}

class JSONDecoder1: JSONDecoder {
    func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : KKDecodable {
         // Some code that would invoke `init(decoder1: Decoder)`
         // which is defined in `KKDecodable`
    }
}

我无法使用此方法编写工作代码

1 个答案:

答案 0 :(得分:6)

属性包装器

您可以使用属性包装器。想象一下这个例子:

@propertyWrapper
struct SomeKindOfBool: Decodable {
    var wrappedValue: Bool?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let stringifiedValue = try? container.decode(String.self) {
            switch stringifiedValue.lowercased() {
            case "false": wrappedValue = false
            case "true": wrappedValue = true
            default: wrappedValue = nil
            }
        } else {
            wrappedValue = try? container.decode(Bool.self)
        }
    }
}

用法

struct MyType: Decodable {
    @SomeKindOfBool var someKey: Bool?
}

您现在可以像普通的someKey一样使用Bool值:

测试:

let jsonData = """
[
 { "someKey": "something else" },
 { "someKey": "true" },
 { "someKey": true }
]
""".data(using: .utf8)!

let decodedJSON = try! JSONDecoder().decode([MyType].self, from: jsonData)

for decodedType in decodedJSON {
    print(decodedType.someKey)
}

结果:

  

     

可选(真)

     

可选(真)


您可以针对其他情况以及所需的任何其他类型执行类似的操作。另外请注意,我已经更改了代码以满足您的需求,但是您可以使用我以gist here in GitHub

发布的更兼容的版本