2017-08-14 55 views
0

比方說,我們有一個JSON結構如下內容(在火力地堡的實時數據庫常用):扁平化JSON當密鑰,只知道在運行時

{ 
    "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個元素的數組:

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對象的RO字符串,例如: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(,)


Decodable不適合這些類型的案件?前

回答

0

基礎機構:

struct BoringEntity: Decodable { 
    let id: String 
    let isActive: Bool 
    let age: Int 
    let company: String 
} 

解決方法1:使用一個額外的結構沒有密鑰

/// 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) 

解決方案2:使用包裝

由於代碼的不同我能我的方法與他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) 
1

一對夫婦的事情,我回答你的問題:

1:註釋(// id)使JSON無效。 JSON不允許評論。

2:idBoringEntity屬性來自哪裏?

struct BoringEntity: Decodable { 
    let id: String   // where is it stored in the JSON??? 
    let isActive: Bool 
    let age: Int 
    let company: String 
} 

如果我忽略了這些東西,你可以在一個結構(BoringEntities)包裹的BoringEntity陣列。直接使用[BoringEntity]並不可取,因爲您必須掩蓋Array的默認init(from decoder:)

這裏的技巧是讓JSONDecoder通過container.allKeys財產給你回的密鑰列表:

struct BoringEntity: Decodable { 
    let isActive: Bool 
    let age: Int 
    let company: String 
} 

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

    init(from decoder: Decoder) throws { 
     let container = try decoder.container(keyedBy: PhantomKeys.self) 

     for key in container.allKeys { 
      let entity = try container.decode(BoringEntity.self, forKey: key) 
      entities.append(entity) 
     } 
    } 
} 

用法:

let jsonData = """ 
{ 
    "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" 
    } 
} 
""".data(using: .utf8)! 

let entities = try JSONDecoder().decode(BoringEntities.self, from: jsonData).entities 
+0

我後來添加的註釋來解釋其中的id屬性是從哪來的,明知它將使JSON無效 – nathan

+0

所以'id'是,例如。 '18348b9b-9a49-4e04-ac35-37e38a8db1e2'? –

+0

確實。我會編輯我的問題,以清除而不使json示例失效 – nathan

相關問題