如何将@propertyWrapper 用于带有可选键的 Decodable?
How can I use @propertyWrapper for Decodable with optional keys?
我正在使用 property wrapper 将字符串“true”和“false”解码为布尔值。我还想将密钥设为可选。因此,如果 JSON 中缺少密钥,则应将其解码为 nil。不幸的是,添加 属性 包装器会破坏它并抛出 Swift.DecodingError.keyNotFound
。
@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)
}
}
}
public struct MyType: Decodable {
@SomeKindOfBool var someKey: Bool?
}
let jsonData = """
[
{ "someKey": true },
{ "someKey": "false" },
{}
]
""".data(using: .utf8)!
let decodedJSON = try! JSONDecoder().decode([MyType].self, from: jsonData)
for decodedType in decodedJSON {
print(decodedType.someKey ?? "nil")
}
知道如何解决这个问题吗?
init(from:)
的合成代码通常在类型可选时使用 decodeIfPresent
。但是,属性 包装器始终是非可选的,并且只能使用可选作为其基础值。这就是为什么合成器总是使用正常的 decode
,如果密钥不存在(good writeup in the Swift Forums)就会失败。
我用优秀的 CodableWrappers package:
解决了这个问题
public struct NonConformingBoolStaticDecoder: StaticDecoder {
public static func decode(from decoder: Decoder) throws -> Bool {
if let stringValue = try? String(from: decoder) {
switch stringValue.lowercased() {
case "false", "no", "0": return false
case "true", "yes", "1": return true
default:
throw DecodingError.valueNotFound(self, DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Expected true/false, yes/no or 0/1 but found \(stringValue) instead"))
}
} else {
return try Bool(from: decoder)
}
}
}
typealias NonConformingBoolDecoding = DecodingUses<NonConformingBoolStaticDecoder>
然后我可以像这样定义我的可解码结构:
public struct MyType: Decodable {
@OptionalDecoding<NonConformingBoolDecoding> var someKey: Bool?
}
我正在使用 property wrapper 将字符串“true”和“false”解码为布尔值。我还想将密钥设为可选。因此,如果 JSON 中缺少密钥,则应将其解码为 nil。不幸的是,添加 属性 包装器会破坏它并抛出 Swift.DecodingError.keyNotFound
。
@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)
}
}
}
public struct MyType: Decodable {
@SomeKindOfBool var someKey: Bool?
}
let jsonData = """
[
{ "someKey": true },
{ "someKey": "false" },
{}
]
""".data(using: .utf8)!
let decodedJSON = try! JSONDecoder().decode([MyType].self, from: jsonData)
for decodedType in decodedJSON {
print(decodedType.someKey ?? "nil")
}
知道如何解决这个问题吗?
init(from:)
的合成代码通常在类型可选时使用 decodeIfPresent
。但是,属性 包装器始终是非可选的,并且只能使用可选作为其基础值。这就是为什么合成器总是使用正常的 decode
,如果密钥不存在(good writeup in the Swift Forums)就会失败。
我用优秀的 CodableWrappers package:
解决了这个问题public struct NonConformingBoolStaticDecoder: StaticDecoder {
public static func decode(from decoder: Decoder) throws -> Bool {
if let stringValue = try? String(from: decoder) {
switch stringValue.lowercased() {
case "false", "no", "0": return false
case "true", "yes", "1": return true
default:
throw DecodingError.valueNotFound(self, DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Expected true/false, yes/no or 0/1 but found \(stringValue) instead"))
}
} else {
return try Bool(from: decoder)
}
}
}
typealias NonConformingBoolDecoding = DecodingUses<NonConformingBoolStaticDecoder>
然后我可以像这样定义我的可解码结构:
public struct MyType: Decodable {
@OptionalDecoding<NonConformingBoolDecoding> var someKey: Bool?
}