Swift Codable:使用父级的键作为值

Pim*_*Pim 2 swift codable decodable jsondecoder

我有一个 JSON,其 ID 在根级别:

{
    "12345": {
        "name": "Pim"
    },
    "54321": {
        "name": "Dorien"
    }
}
Run Code Online (Sandbox Code Playgroud)

我的目标是使用 Codable 创建一个同时具有 name 和 ID 属性的 User 对象数组。

struct User: Codable {
    let id: String
    let name: String
}
Run Code Online (Sandbox Code Playgroud)

我知道如何使用带有单个根级别密钥的Codable ,并且知道如何使用未知密钥。但我在这里尝试做的是两者的结合,我不知道下一步该做什么。

这是我到目前为止得到的:(您可以将其粘贴到 Playground 中)

import UIKit

var json = """
{
    "12345": {
        "name": "Pim"
    },
    "54321": {
        "name": "Dorien"
    }
}
"""

let data = Data(json.utf8)

struct User: Codable {
    let name: String
}

let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([String: User].self, from: data)
    decoded.forEach { print($0.key, $0.value) }
    // 54321 User(name: "Dorien")
    // 12345 User(name: "Pim")
} catch {
    print("Failed to decode JSON")
}
Run Code Online (Sandbox Code Playgroud)

这就是我想做的:

let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode([User].self, from: data)
    decoded.forEach { print($0) }
    // User(id: "54321", name: "Dorien")
    // User(id: "12345", name: "Pim")
} catch {
    print("Failed to decode JSON")
}
Run Code Online (Sandbox Code Playgroud)

任何帮助是极大的赞赏。

Kam*_*ran 5

您可以使用自定义编码密钥并如下设置用户来解析未知密钥,

struct CustomCodingKey: CodingKey {

    let intValue: Int?
    let stringValue: String

    init?(stringValue: String) {
        self.intValue = Int(stringValue)
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        self.intValue = intValue
        self.stringValue = "\(intValue)"
    }
}

struct UserInfo: Codable {
    let name: String
}

struct User: Codable {
    var id: String = ""
    var info: UserInfo?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CustomCodingKey.self)
        if let key = container.allKeys.first {
            self.id = key.stringValue
            self.info = try container.decode(UserInfo.self, forKey: key)
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CustomCodingKey.self)
        if let key = CustomCodingKey(stringValue: self.id) {
            try container.encode(self.info, forKey: key)
        }
    }
}

let decoder = JSONDecoder()
do {
    let decoded = try decoder.decode(User.self, from: data)
    print(decoded.id) // 12345
    print(decoded.info!.name) // Pim
} catch {
    print("Failed to decode JSON")
}
Run Code Online (Sandbox Code Playgroud)