【问题标题】:Swift Decodable Optional KeySwift 可解码可选密钥
【发布时间】:2018-03-26 04:40:24
【问题描述】:

(这是这个问题的后续: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 个示例都工作? nestedContainer 有类似decodeIfPresent 的东西吗?

【问题讨论】:

    标签: ios json swift swift4 codable


    【解决方案1】:

    您可以使用以下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
            }
        }
    }
    

    【讨论】:

    • 没有初始化器有什么解决办法吗?
    • @ShreeshaKedlaya 没有init(from:) 的唯一方法是使用嵌套结构,就像其他答案一样,或者使用诸如dgrzeszczak/KeyedCodable 之类的库。
    【解决方案2】:

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

    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(检查容器是否存在。

    【讨论】:

    • 需要这个,谢谢!
    【解决方案3】:

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

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

    使UserInfo.ageAge.realage 选项有效,如果这是您想要完成的。

    【讨论】:

    • 我正在尝试使用 1 个类/结构来实现这一点。我有特定的理由用 1 个类/结构来设计它。请参阅我的问答here 了解更多信息。
    • 啊,我明白了——我必须同意那里的答案,即建议与 JSON 结构相对应的“原始”模型,您可以将其映射到具有您想要的特殊行为的更高级别模型!我认为这可能比实现自定义解码器更容易维护和理解,但无论哪种方式都祝你好运!
    • 就像我的数据在我的服务器文件中的结构方式一样,这使得使用自定义解码器变得更加容易。就我的理解和结构而言,这样做要容易得多。也许有人将能够给出符合我想要的答案。感谢您的帮助。
    猜你喜欢
    • 2018-01-17
    • 1970-01-01
    • 2017-11-26
    • 1970-01-01
    • 2012-02-12
    • 1970-01-01
    • 2014-03-25
    • 2018-10-27
    相关资源
    最近更新 更多