仅在运行时知道键时展平JSON [英] Flattening JSON when keys are known only at runtime

查看:87
本文介绍了仅在运行时知道键时展平JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们具有如下所示的JSON结构(通常在Firebase的实时数据库中使用):

Let's say we have a JSON structure like the following (commonly used in Firebase's Realtime Database):

{
  "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个元素的数组:

Expected solution:

Using Decodable I'd like to convert it into an array of 3 elements:

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.

The id attribute corresponds to the json object's root string , e.g: 18348b9b-9a49-4e04-ac35-37e38a8db1e2.

我已经尝试了几种方法,但是在没有辅助实体(或使用可选对象)的情况下无法获得id属性:

I have already tried several approaches but couldn't get the id attribute without requiring an auxiliary entity (or using optionals):

/// 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(,)由于密钥未知而无法使用.

Using init(from: Decoder) isn't as trivial as in other cases since keyedContainer(,) can't be used due to the fact that the key is unknown.


Decodable是否不适用于此类情况?

Is Decodable unsuited for these types of cases ?

推荐答案

基本实体:

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想法结合起来,但是却无法解决:必须始终使用额外的实体.


Solution 2: Using a wrapper

Thanks to Code Different I was able to combine my approach with his PhantomKeys idea, but there's no way around it: an extra entity must always be used.

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)

这篇关于仅在运行时知道键时展平JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆