【问题标题】:How can I use @propertyWrapper for Decodable with optional keys?如何将@propertyWrapper 用于带有可选键的Decodable?
【发布时间】:2021-12-26 21:53:35
【问题描述】:

我使用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")
}

知道如何解决这个问题吗?

【问题讨论】:

  • @SPatel 这个服务不使用属性包装器,所以它不是很有帮助。
  • 如果将 someKey 的类型更改为非可选会发生什么?
  • 同样的错误。然后我再也无法区分 false 和 nil(也就是不存在)。
  • 好的,我尝试运行你的代码,但它在你的 json 中的最后一个条目上失败,{},我不确定它应该是什么?下次您发布错误消息时,请发布完整消息。
  • 你试过用decodeIfPresent代替吗?

标签: swift decodable property-wrapper


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    相关资源
    最近更新 更多