说JSON看起来像这样:
[
{
"data": {
"children": [
{
"name": "Ralph"
},
{
"name": "Woofer"
}
]
}
},
{
"data": {
"children": [
{
"name": "Spot"
},
{
"name": "Trevor"
}
]
}
}
]
你有这个非常奇怪的结构,其中根项是一个数组,有两个对象,这两个对象中的每一个都是Dog
字典数组。
但问题是Dog
数组是两个键!您必须通过data
和children
才能访问它。我saw this answer描绘的是用一个键深度来做它,但是当它嵌套两个深时我似乎无法重现结果。
我希望结果(看起来很奇怪)是这样的,两个列表都是分开维护的:
struct Result: Codable {
let dogs1: [Dog]
let dogs2: [Dog]
}
我知道我需要一个自定义初始化器/解码器,但我不确定如何访问它。
答案 0 :(得分:1)
所以,简短的回答是:你不能,而答案很长。
TL;博士
剥离皮肤的一种方法是从结构的中间表示开始。像这样:
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结构相同,因为名称dogs1
和dogs2
在数据中没有出现,您无法在以下位置构成属性名称运行时(好吧,在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