如何在Swift中解码JSON,它是一个带有双嵌套项的数组?

时间:2018-05-30 15:00:37

标签: json swift struct swift4 jsondecoder

说JSON看起来像这样:

[
    {
      "data": {
        "children": [
          {
            "name": "Ralph"
          },
          {
            "name": "Woofer"
          }
        ]
      }
    },
    {
      "data": {
        "children": [
          {
            "name": "Spot"
          },
          {
            "name": "Trevor"
          }
        ]
      }
    }
]

你有这个非常奇怪的结构,其中根项是一个数组,有两个对象,这两个对象中的每一个都是Dog字典数组。

但问题是Dog数组是两个键!您必须通过datachildren才能访问它。我saw this answer描绘的是用一个键深度来做它,但是当它嵌套两个深时我似乎无法重现结果。

我希望结果(看起来很奇怪)是这样的,两个列表都是分开维护的:

struct Result: Codable {
    let dogs1: [Dog]
    let dogs2: [Dog]
}

我知道我需要一个自定义初始化器/解码器,但我不确定如何访问它。

3 个答案:

答案 0 :(得分:1)

所以,简短的回答是:你不能,而答案很长。

TL;博士

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

剥离皮肤的一种方法是从结构的中间表示开始。像这样:

struct Intermediate: Codable { struct Dog: Codable { let name: String } struct Children: Codable { let children: [Dog] } let data: Children }

然后您可以将其转换为您的Result结构。您可以将Result结构转换为序列化的中间结构。这可以让你逃避更复杂的使用conding键和编码器。如果您不希望任何人捅它,您可以将中间表示保密在您的模块中。

答案 1 :(得分:1)

使用中间结构进行dumpster-dive并收集所需的数据,然后将其丢弃。

所以,从最高级别声明的Dog结构开始:

struct Dog : Decodable { let name : String }

在实际代码中,创建临时本地结构以包装它并解码JSON:

struct TheChildren : Decodable { let children : [Dog] }
struct TheData : Decodable { let data : TheChildren }
let arr = try! JSONDecoder().decode([TheData].self, from: yourJSONdata)

现在只需拔出所需的狗:

let dogs = arr.map {$0.data.children}
/*
[[Dog(name: "Ralph"), Dog(name: "Woofer")], 
 [Dog(name: "Spot"), Dog(name: "Trevor")]]
*/

这是一系列Dogs数组,因此两个"数组都是分开维护的#34;因为它们是结果数组的独立元素。这似乎是一个非常合理的表现。

现在,如果你想将这些信息进一步填充到一个新的结构中,那很好。它不会与您提出的Result结构相同,因为名称dogs1dogs2在数据中没有出现,您无法在以下位置构成属性名称运行时(好吧,在Swift 4.2中你可以,但这是另一个故事)。但重点是,您可以轻松获得Dog数据,而且没有额外的材料。并且没有真正的理由为什么以名称dogs1访问第一个数组比通过索引获取dogs[0]更好;实际上,后者实际上更好。使用索引号结束属性名称​​总是一个糟糕的气味,表明您真正需要的是某种类型的集合。

答案 2 :(得分:1)

您可以解码该JSON而无需引入中间结构,同时通过将唯一键为Dictionary的外部data解码为类型为{{1}的嵌套Dictionary来保持类型安全性这是非常混乱的,但是因为在外部词典中只有2个嵌套层和单个键,所以很有效。

[String:[String:[Dog]]]

然后您可以简单地解码struct Dog: Codable { let name:String } struct Result: Codable { let dogs1: [Dog] let dogs2: [Dog] enum DogJSONErrors: String, Error { case invalidNumberOfContainers case noChildrenContainer } init(from decoder: Decoder) throws { var containersArray = try decoder.unkeyedContainer() guard containersArray.count == 2 else { throw DogJSONErrors.invalidNumberOfContainers} let dogsContainer1 = try containersArray.decode([String:[String:[Dog]]].self) let dogsContainer2 = try containersArray.decode([String:[String:[Dog]]].self) guard let dogs1 = dogsContainer1["data"]?["children"], let dogs2 = dogsContainer2["data"]?["children"] else { throw DogJSONErrors.noChildrenContainer} self.dogs1 = dogs1 self.dogs2 = dogs2 } } 实例,如下所示:

Result