19

(这是这个问题的后续:Using Decodable protocol with multiples keys。)

我有以下 Swift 代码:

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

我知道如果我使用decodeIfPresent并且没有该属性,如果它是可选变量,它仍然会正确处理它。

例如,以下 JSON 可以使用上面的代码对其进行解析。

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

以下 JSON 也可以使用。

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

但以下不起作用。

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

我怎样才能使所有 3 个示例都起作用?有没有类似于decodeIfPresentfor 的东西nestedContainer

4

3 回答 3

60

您可以使用以下KeyedDecodingContainer功能:

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

返回一个Bool值,该值指示解码器是否包含与给定键关联的值。与给定键关联的值可以是适合数据格式的空值。

例如,在请求相应的嵌套容器之前"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
        }
    }
}
于 2017-10-14T13:23:07.033 回答
13

我遇到了这个问题,我找到了这个解决方案,以防万一对其他人有帮助:

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

如果您有可选容器,try? values.nestedContainer(keyedBy:forKey)则无需使用 . 检查容器是否存在contains(

于 2018-10-31T10:22:54.630 回答
2

您可以尝试将示例 JSON 粘贴到quicktype中以查看它推断出的类型吗?根据您的问题,我粘贴了您的示例并得到:

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

struct Age: Codable {
    let realage: Int?
}

如果这就是您要完成的工作,那么制作UserInfo.age和可选项可以工作。Age.realage

于 2017-10-14T05:15:24.360 回答