当 Swift 中可选时,将 JSON 中的类型不匹配键解码为 nil 的一般策略
General strategy to decode type mismatch keys in JSON into nil when optional in Swift
这是我的问题,当我收到一些 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)
}
}
然后你可以有选择地包装你想要的属性 special-handle:
struct Foo : Decodable {
@NilOnTypeMismatch
var bar : Int?
}
更全面的方法是将 KeyedDecodingContainer
扩展 Int
,但这将适用 app-wide:
extension KeyedDecodingContainer {
func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
try? decode(Int.self, forKey: key)
}
}
不幸的是,我认为不可能(或不知道如何)使其成为泛型,因为我的猜测是在使用泛型时此函数重载的优先级低于默认实现。
这是我的问题,当我收到一些 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)
}
}
然后你可以有选择地包装你想要的属性 special-handle:
struct Foo : Decodable {
@NilOnTypeMismatch
var bar : Int?
}
更全面的方法是将 KeyedDecodingContainer
扩展 Int
,但这将适用 app-wide:
extension KeyedDecodingContainer {
func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
try? decode(Int.self, forKey: key)
}
}
不幸的是,我认为不可能(或不知道如何)使其成为泛型,因为我的猜测是在使用泛型时此函数重载的优先级低于默认实现。