在 Swift 中可选时将 JSON 中的类型不匹配键解码为 nil 的一般策略

2024-01-02

这是我的问题,当我收到一些 JSON 时,某些值与所需的类型不匹配。我真的不介意,我只对类型正确时的值感兴趣。

例如下面的结构:

struct Foo : Decodable {
    var bar : Int?
}

我希望它能够匹配这些 JSON:

{ "bar" : 42 }    => foo.bar == 42
{ "bar" : null }  => foo.bar == nil
{ "bar" : "baz" } => foo.bar == nil

事实上我正在寻找一个可选的Int,所以只要它是一个整数我就想要它,但是当它是null或者我想要的其他东西nil.

不幸的是,我们的好老JSONDecoder在最后一种情况下引发类型不匹配错误。

我知道一种手动方法:

struct Foo : Decodable {
    var bar : Int?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        self.bar = try? container.decode(Int.self, forKey: .bar)
    }
    
    enum CodingKeys : CodingKey {
        case bar
    }
}

但我有很多结构和很多字段需要检查。

所以我想知道是否有一种通用的方法可以做到这一点,例如:

decoder.typeMismatchStrategy = .nilInsteadOfError // <= Don't try it at home, I know it does not exist...

或者也许覆盖JSONDecoder,无论如何,要写一次而不是在每个结构上。

提前致谢。


一种方法是创建一个属性包装器Decodable用于这些属性:

@propertyWrapper
struct NilOnTypeMismatch<Value> {
    var wrappedValue: Value?
}

extension NilOnTypeMismatch: Decodable where Value: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.wrappedValue = try? container.decode(Value.self)
    }
}

然后你可以有选择地包装你想要特殊处理的属性:

struct Foo : Decodable {
    @NilOnTypeMismatch
    var bar : Int?
}

更全面的方法是扩展KeyedDecodingContainer for Ints,但这适用于整个应用程序:

extension KeyedDecodingContainer {
    func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
        try? decode(Int.self, forKey: key)
    }
}

不幸的是,我认为不可能(或不知道如何)使其通用,因为我的猜测是,在使用泛型时,此函数重载的优先级低于默认实现。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Swift 中可选时将 JSON 中的类型不匹配键解码为 nil 的一般策略 的相关文章

随机推荐