使用具有多个键的可解码协议

Cha*_*ish 1 json swift swift4 codable

假设我有以下代码:

import Foundation

let jsonData = """
[
    {"firstname": "Tom", "lastname": "Smith", "age": {"realage": "28"}},
    {"firstname": "Bob", "lastname": "Smith", "age": {"fakeage": "31"}}
]
""".data(using: .utf8)!

struct Person: Codable {
    let firstName, lastName: String
    let age: String?

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

let decoded = try JSONDecoder().decode([Person].self, from: jsonData)
print(decoded)
Run Code Online (Sandbox Code Playgroud)

一切都在工作,除了age总是nil。这是有道理的。我的问题是如何在第一个示例和第二个示例中设置人的年龄 =realage或。取而代之的是在这两种情况下,我想这是在第一种情况。28nilagenil28

有没有办法只使用CodingKeys而不必添加另一个结构或类来实现这一点?如果不是,我如何使用另一个结构或类以最简单的方式实现我想要的?

Cod*_*ent 5

在解码嵌套 JSON 数据时,我最喜欢的方法是定义一个与 JSON 非常接近的“原始”模型,即使snake_case需要也可以使用。它有助于将 JSON 数据快速导入 Swift,然后您可以使用 Swift 进行您需要的操作:

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

    // This matches the keys in the JSON so we don't have to write custom CodingKeys    
    private struct RawPerson: Decodable {
        struct RawAge: Decodable {
            let realage: String?
            let fakeage: String?
        }

        let firstname: String
        let lastname: String
        let age: RawAge
    }

    init(from decoder: Decoder) throws {
        let rawPerson  = try RawPerson(from: decoder)
        self.firstName = rawPerson.firstname
        self.lastName  = rawPerson.lastname
        self.age       = rawPerson.age.realage
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,我建议您谨慎使用Codable,因为它同时包含EncodableDecodable。似乎您只需Decodable要使您的模型符合该协议即可。