假设我们有一个类似以下的JSON结构(通常在Firebase的实时数据库中使用):
{ "18348b9b-9a49-4e04-ac35-37e38a8db1e2": { "isActive": false, "age": 29, "company": "BALOOBA" }, "20aca96e-663a-493c-8e9b-cb7b8272f817": { "isActive": false, "age": 39, "company": "QUONATA" }, "bd0c389b-2736-481a-9cf0-170600d36b6d": { "isActive": false, "age": 35, "company": "EARTHMARK" } }
使用Decodable我想将其转换为3个元素的数组:
Decodable
struct BoringEntity: Decodable { let id: String let isActive: Bool let age: Int let company: String init(from decoder: Decoder) throws { // ... } } let entities: [BoringEntity] = try! JSONDecoder()...
该 ID 属性对应于JSON对象的根弦,例如:18348b9b-9a49-4e04-ac35-37e38a8db1e2。
18348b9b-9a49-4e04-ac35-37e38a8db1e2
我已经尝试了几种方法,但是在不需要辅助实体(或使用可选对象)的情况下无法获得id属性:
/// Incomplete BoringEntity version to make Decodable conformance possible. struct BoringEntityIncomplete: Decodable { let isActive: Bool let age: Int let company: String } // Decode to aux struct let decoded = try! JSONDecoder().decode([String : BoringEntityIncomplete].self, for: jsonData) // Map aux entities to BoringEntity let entities = decoded.map { BoringEntity(...) }
使用init(from: Decoder)不像其他情况那么简单,因为keyedContainer(,)由于密钥未知,因此无法使用。
init(from: Decoder)
keyedContainer(,)
是Decodable不适合于这些类型的案件?
基础实体:
struct BoringEntity: Decodable { let id: String let isActive: Bool let age: Int let company: String }
/// Incomplete BoringEntity version to make Decodable conformance possible. private struct BoringEntityBare: Decodable { let isActive: Bool let age: Int let company: String } // Decode to aux struct private let decoded = try! JSONDecoder().decode([String : BoringEntityBare].self, from: jsonData) // Map aux entities to BoringEntity let entities = decoded.map { BoringEntity(id: $0.key, isActive: $0.value.isActive, age: $0.value.age, company: $0.value.company) } print(entities)
多亏了Code Different,我得以将自己的方法与他的PhantomKeys想法结合起来,但是却无法解决:必须始终使用额外的实体。
PhantomKeys
struct BoringEntities: Decodable { var entities = [BoringEntity]() // This really is just a stand-in to make the compiler happy. // It doesn't actually do anything. private struct PhantomKeys: CodingKey { var intValue: Int? var stringValue: String init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" } init?(stringValue: String) { self.stringValue = stringValue } } private enum BareKeys: String, CodingKey { case isActive, age, company } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: PhantomKeys.self) // There's only one key for key in container.allKeys { let aux = try container.nestedContainer(keyedBy: BareKeys.self, forKey: key) let age = try aux.decode(Int.self, forKey: .age) let company = try aux.decode(String.self, forKey: .company) let isActive = try aux.decode(Bool.self, forKey: .isActive) let entity = BoringEntity(id: key.stringValue, isActive: isActive, age: age, company: company) entities.append(entity) } } } let entities = try JSONDecoder().decode(BoringEntities.self, from: jsonData).entities print(entities)