Swift Codable: используйте родительский ключ в качестве значения - PullRequest
1 голос
/ 07 октября 2019

У меня есть JSON с идентификаторами на корневом уровне:

{
    "12345": {
        "name": "Pim"
    },
    "54321": {
        "name": "Dorien"
    }
}

Моя цель - использовать Codable для создания массива объектов User, имеющих свойства как name, так и ID.

struct User: Codable {
    let id: String
    let name: String
}

Я знаю, как использовать Codable с одним ключом корневого уровня , и я знаю, как работать с неизвестными ключами . Но то, что я пытаюсь сделать здесь, это комбинация обоих, и я понятия не имею, что делать дальше.

Вот что я получил до сих пор: (Вы можете вставить это на игровой площадке)

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")
}

Вот что я хотел бы сделать:

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")
}

Любая помощь очень ценится.

1 Ответ

2 голосов
/ 07 октября 2019

Вы можете использовать пользовательский ключ кодирования и настроить пользователя, как показано ниже для анализа неизвестных ключей,

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")
}
...