我如何解码 Swift 中的 JSON,它是一个包含双重嵌套项目的数组?

How do I decode JSON in Swift where it's an array with double-nested items?

假设 JSON 看起来像这样:

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

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

但问题是Dog数组是两个key in!您必须通过 datachildren 才能到达。我 描述了用一个键深度来完成它,但是当它嵌套两个深度时我似乎无法重现结果。

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

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

我知道我需要自定义 initializer/decoder,但我不确定如何访问它。

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

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 结构转换为用于序列化的中间结构。这使您可以避免使用更复杂的配置键和编码器。如果你不想让任何人戳它,你可以在你的模块中保持中间表示私有。

使用中间结构进行垃圾回收并收集所需数据,然后处理它们。

因此,从在顶层声明的 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 {[=12=].data.children}
/*
[[Dog(name: "Ralph"), Dog(name: "Woofer")], 
 [Dog(name: "Spot"), Dog(name: "Trevor")]]
*/

那是狗数组的数组,所以两者 "arrays are maintained separately" 因为它们是结果数组的独立元素。这似乎是一个完全合理的表示。

现在,如果您想将该信息进一步填充到新结构中,没问题。它不会与您提出的结果结构相同,因为名称 dogs1dogs2 没有出现在数据中,并且您不能在以下位置组成 属性 名称运行时(好吧,在 Swift 4.2 中你可以,但那是另一回事了)。但关键是,您已经轻松获得了 Dog 数据,并且没有额外的 material。并且没有真正的理由为什么访问名称 dogs1 下的第一个数组比通过索引获取它更好 dogs[0];事实上,后者实际上更好。以索引号结尾的 属性 名称 总是 难闻的气味,表明您真正需要的是某种集合。

您可以解码 JSON 而无需引入中间结构,同时通过将唯一键为 data 的外部 Dictionary 解码为嵌套 Dictionary 来保持类型安全属于 [String:[String:[Dog]]] 类型,这非常混乱,但可以工作,因为您在外部词典中只有 2 个嵌套层和单个键。

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 实例:

do {
    let dogResults = try JSONDecoder().decode(Result.self, from: dogsJSONString.data(using: .utf8)!)
    print(dogResults.dogs1,dogResults.dogs2)
} catch {
    print(error)
}