简体   繁体   English

Swift 可解码可选密钥

[英]Swift Decodable Optional Key

(This is a follow-up from this question: Using Decodable protocol with multiples keys .) (这是这个问题的后续: Using Decodable protocol with multiples keys 。)

I have the following Swift code:我有以下 Swift 代码:

let additionalInfo = try values.nestedContainer(keyedBy: UserInfoKeys.self, forKey: .age)
age = try additionalInfo.decodeIfPresent(Int.self, forKey: .realage)

I know that if I use decodeIfPresent and don't have the property it will still handle it correctly if it's an optional variable.我知道如果我使用decodeIfPresent并且没有属性,如果它是一个可选变量,它仍然会正确处理它。

For example the following JSON works to parse it using the code above.例如,下面的 JSON 可以使用上面的代码解析它。

{
    "firstname": "Test",
    "lastname": "User",
    "age": {"realage": 29}
}

And the following JSON works as well.以下 JSON 也适用。

{
    "firstname": "Test",
    "lastname": "User",
    "age": {"notrealage": 30}
}

But the following doesn't work.但以下不起作用。

{
    "firstname": "Test",
    "lastname": "User"
}

How can I make all 3 examples work?我怎样才能让所有 3 个例子都有效? Is there something similar to decodeIfPresent for nestedContainer ?是否有类似于decodeIfPresent用于nestedContainer

You can use the following KeyedDecodingContainer function:您可以使用以下KeyedDecodingContainer函数:

func contains(_ key: KeyedDecodingContainer.Key) -> Bool

Returns a Bool value indicating whether the decoder contains a value associated with the given key.返回一个Bool值,指示解码器是否包含与给定键关联的值。 The value associated with the given key may be a null value as appropriate for the data format.与给定键关联的值可以是适合数据格式的空值。

For instance, to check if the "age" key exists before requesting the corresponding nested container:例如,请求相应的嵌套容器之前检查"age"键是否存在:

struct Person: Decodable {
    let firstName, lastName: String
    let age: Int?

    enum CodingKeys: String, CodingKey {
        case firstName = "firstname"
        case lastName = "lastname"
        case age
    }

    enum AgeKeys: String, CodingKey {
        case realAge = "realage"
        case fakeAge = "fakeage"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.firstName = try values.decode(String.self, forKey: .firstName)
        self.lastName = try values.decode(String.self, forKey: .lastName)

        if values.contains(.age) {
            let age = try values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
            self.age = try age.decodeIfPresent(Int.self, forKey: .realAge)
        } else {
            self.age = nil
        }
    }
}

I had this issue and I found this solution, just in case is helpful to somebody else:我遇到了这个问题,我找到了这个解决方案,以防万一对其他人有帮助:

let ageContainer = try? values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try ageContainer?.decodeIfPresent(Int.self, forKey: .realAge)

If you have an optional container, using try? values.nestedContainer(keyedBy:forKey)如果你有一个可选的容器,使用try? values.nestedContainer(keyedBy:forKey) try? values.nestedContainer(keyedBy:forKey) you don't need to check if the container exist using contains( . try? values.nestedContainer(keyedBy:forKey)你不需要使用contains(来检查容器是否存在。

Can you try pasting your sample JSON into quicktype to see what types it infers?您可以尝试将示例 JSON 粘贴到quicktype 中以查看它推断出哪些类型吗? Based on your question, I pasted your samples and got:根据您的问题,我粘贴了您的样本并得到:

struct UserInfo: Codable {
    let firstname: String
    let age: Age?
    let lastname: String
}

struct Age: Codable {
    let realage: Int?
}

Making UserInfo.age and Age.realage optionals works, if that's what you're trying to accomplish.使UserInfo.ageAge.realage选项起作用,如果这是您要完成的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM