swift:如何在不创建包含所述对象数组的结构的情况下解码 json 对象数组?
swift: How can I decode an array of json objects, without creating a struct, that holds an array of said objects?
我的数据是这样的:
"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 建议:https://app.quicktype.io?share=j22hopuBnkuHZziOSvxG
而只是在“地点”列表中解码。这样就可以了:
let places = try JSONDecoder().decode([Place].self, from: data)
目前我发现的可能解决方案:
'Scuffed'解决方法:
创建通用可解码数组结构:
https://swiftsenpai.com/swift/decode-dynamic-keys-json/
如果您发现自己多次需要这个,那么您可以构建自己的通用结构,对它找到的任何密钥进行解码:
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 }
}
}
有了这个,您可以扩展 JSONDecoder
以获得更好的呼叫站点:
extension JSONDecoder {
func decode<T: Decodable>(nested: [T].Type, from data: Data) throws -> [T] {
try decode(Nester<T>.self, from: data).elements
}
}
那么就只需要调用新的重载了:
let places = try JSONDecoder().decode(nested: [Place].self, from: data)
P.S。如果你愿意,你可以在扩展中隐藏复杂的结构,结果是这样的:
extension JSONDecoder {
func decode<T: Decodable>(nested: [T].Type, from data: Data) throws -> [T] {
try decode(Nester<T>.self, from: data).elements
}
private 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 {
throw DecodingError.typeMismatch([String:Any].self, DecodingError.Context(codingPath: [], debugDescription: "Expected to find at least one key"))
}
}
struct CodingKeys: CodingKey {
let stringValue: String
var intValue: Int? { nil }
init?(stringValue: String) {
self.stringValue = stringValue
}
init?(intValue: Int) { return nil }
}
}
}
缺点是,如果您想扩展 JSON 解码器之外的其他解码器,您将无法重用该结构。
有一种方法可以避免顶级结构 (Places
),方法是将 JSON 解码为 [String: [Place]]
类型,然后获取字典的第一个元素 values
属性:
let decoder = JSONDecoder()
do {
let places = try decoder.decode([String: [Place]].self, from: data)
print(places.values.first ?? [])
} catch {
print(error)
}
我的数据是这样的:
"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 建议:https://app.quicktype.io?share=j22hopuBnkuHZziOSvxG 而只是在“地点”列表中解码。这样就可以了:
let places = try JSONDecoder().decode([Place].self, from: data)
目前我发现的可能解决方案:
'Scuffed'解决方法:
创建通用可解码数组结构: https://swiftsenpai.com/swift/decode-dynamic-keys-json/
如果您发现自己多次需要这个,那么您可以构建自己的通用结构,对它找到的任何密钥进行解码:
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 }
}
}
有了这个,您可以扩展 JSONDecoder
以获得更好的呼叫站点:
extension JSONDecoder {
func decode<T: Decodable>(nested: [T].Type, from data: Data) throws -> [T] {
try decode(Nester<T>.self, from: data).elements
}
}
那么就只需要调用新的重载了:
let places = try JSONDecoder().decode(nested: [Place].self, from: data)
P.S。如果你愿意,你可以在扩展中隐藏复杂的结构,结果是这样的:
extension JSONDecoder {
func decode<T: Decodable>(nested: [T].Type, from data: Data) throws -> [T] {
try decode(Nester<T>.self, from: data).elements
}
private 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 {
throw DecodingError.typeMismatch([String:Any].self, DecodingError.Context(codingPath: [], debugDescription: "Expected to find at least one key"))
}
}
struct CodingKeys: CodingKey {
let stringValue: String
var intValue: Int? { nil }
init?(stringValue: String) {
self.stringValue = stringValue
}
init?(intValue: Int) { return nil }
}
}
}
缺点是,如果您想扩展 JSON 解码器之外的其他解码器,您将无法重用该结构。
有一种方法可以避免顶级结构 (Places
),方法是将 JSON 解码为 [String: [Place]]
类型,然后获取字典的第一个元素 values
属性:
let decoder = JSONDecoder()
do {
let places = try decoder.decode([String: [Place]].self, from: data)
print(places.values.first ?? [])
} catch {
print(error)
}