swift:我如何解码json对象数组,而不创建一个包含所述对象数组的结构?
我的数据如下所示:swift:我如何解码json对象数组,而不创建一个包含所述对象数组的结构?,json,swift,jsondecoder,json-flattener,Json,Swift,Jsondecoder,Json Flattener,我的数据如下所示: "places": [ { "id": 15, "name": "København", "typeId": 6, "coordinates": { "lat": "55.6760968
"places": [
{
"id": 15,
"name": "København",
"typeId": 6,
"coordinates": {
"lat": "55.6760968",
"lng": "12.5683372"
},
"count": 2779
},
{
"id": 19,
"name": "København S",
"typeId": 3,
"coordinates": {
"lat": "55.6508754",
"lng": "12.5991891"
},
"count": 1168
}
]
我希望避免这种情况:
struct Places: Decodable {
let places: [Place]
}
这是QuickType.io建议的:
而是在“地点”列表中解码。这样做可以:
let places = try JSONDecoder().decode([Place].self, from: data)
到目前为止,我已经找到了可能的解决方案:
如果您发现自己多次需要此功能,那么您可以构建自己的通用结构,该结构可以对找到的任何键进行解码:
struct Nester<T: Decodable>: Decodable {
let elements: [T]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let key = container.allKeys.first {
elements = try container.decode([T].self, forKey: key)
} else {
// we run into an empty dictionary, let's signal this
throw DecodingError.typeMismatch([String:Any].self, DecodingError.Context(codingPath: [], debugDescription: "Expected to find at least one key"))
}
}
// A coding key that accepts whatever string value it is given
struct CodingKeys: CodingKey {
let stringValue: String
var intValue: Int? { nil }
init?(stringValue: String) {
self.stringValue = stringValue
}
init?(intValue: Int) { return nil }
}
}
然后,只需调用新的重载:
let places=try JSONDecoder().decode(嵌套:[Place].self,from:data)
另外,如果需要,可以在扩展中隐藏复杂结构,结果如下:
"places": [
{
"id": 15,
"name": "København",
"typeId": 6,
"coordinates": {
"lat": "55.6760968",
"lng": "12.5683372"
},
"count": 2779
},
{
"id": 19,
"name": "København S",
"typeId": 3,
"coordinates": {
"lat": "55.6508754",
"lng": "12.5991891"
},
"count": 1168
}
]
扩展JSONDecoder{
func decode(嵌套:[T]。类型,来自数据:data)抛出->[T]{
尝试解码(Nester.self,from:data).elements
}
私有结构嵌套器:可解码{
let元素:[T]
init(来自解码器:解码器)抛出{
let container=try decoder.container(keyedBy:CodingKeys.self)
如果let key=container.allkey.first{
elements=try container.decode([T].self,forKey:key)
}否则{
抛出DecodingError.typeMismatch([String:Any].self,DecodingError.Context(编码路径:[],调试说明:“应至少找到一个键”))
}
}
结构编码键:编码键{
让stringValue:String
var intValue:Int?{nil}
初始化?(stringValue:String){
self.stringValue=stringValue
}
init?(intValue:Int){return nil}
}
}
}
缺点是,如果要扩展JSON以外的其他解码器,则无法重用该结构。有一种可能的方法可以避免顶级结构(
Places
),方法是将JSON解码为[String:[Place]]
类型,然后获取字典value
属性的第一个元素:
let decoder = JSONDecoder()
do {
let places = try decoder.decode([String: [Place]].self, from: data)
print(places.values.first ?? [])
} catch {
print(error)
}
不,你不能。可以说,你需要从头开始。但是为什么这是一个问题,你可以很容易地访问并只保留Place数组?@JoakimDanielson我只是觉得有一个结构很难看,只是为了正确地解码我的json。我担心情况就是这样。
让places=try JSONDecoder().decode(places.self,from:data)。places
我开始使用Cristik的答案,但是当我继续为API实现webRepo的其余部分时,我发现上面提到的情况是一个边缘情况,除了一个struct/API调用之外,不适用于任何东西。因此,我最终使用了gcharita提到的方法,它将我的对象解码为[String:[object]]总体而言,我认为Cristik的方法更优雅,如果我需要的话,我会使用它的解决方案。因为我的想法是使用一个不太混乱的名称空间,所以我选择了gcharita的.Cleanear而不是我的解决方案,这增加了新结构的开销:拇指支持:关于JSONDecoder
扩展的想法有利于重用。将这两个答案结合起来会得到一个有趣的结果:)