我正在尝试解析一个可以是
I am trying to parse a JSON array which can be
{ "config_data": [ { "name": "illuminate", "config_title": "Blink" }, { "name": "shoot", "config_title": "Fire" } ] }也可以是以下类型
{ "config_data": [ "illuminate", "shoot" ] }甚至
{ "config_data": [ 25, 100 ] }因此,为了使用JSONDecoder对此进行解析,我创建了一个结构如下-
So to parse this using JSONDecoder I created a struct as follows -
Struct Model: Codable { var config_data: [Any]? enum CodingKeys: String, CodingKey { case config_data = "config_data" } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) config_data = try values.decode([Any].self, forKey: .config_data) } }但这无法正常工作,因为任何都无法确认可解码协议.这可能是解决方案.数组可以包含任何类型的数据
But this would not work since Any does not confirm to decodable protocol. What could be the solution for this. The array can contain any kind of data
推荐答案我使用了 quicktype 推断config_data的类型,它建议为您的对象,字符串和整数值使用单独的大小写的枚举:
I used quicktype to infer the type of config_data and it suggested an enum with separate cases for your object, string, and integer values:
struct ConfigData { let configData: [ConfigDatumElement] } enum ConfigDatumElement { case configDatumClass(ConfigDatumClass) case integer(Int) case string(String) } struct ConfigDatumClass { let name, configTitle: String }这是完整的代码示例.解码enum有点棘手,但是quicktype可以帮助您解决问题:
Here's the complete code example. It's a bit tricky to decode the enum but quicktype helps you out there:
// To parse the JSON, add this file to your project and do: // // let configData = try? JSONDecoder().decode(ConfigData.self, from: jsonData) import Foundation struct ConfigData: Codable { let configData: [ConfigDatumElement] enum CodingKeys: String, CodingKey { case configData = "config_data" } } enum ConfigDatumElement: Codable { case configDatumClass(ConfigDatumClass) case integer(Int) case string(String) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let x = try? container.decode(Int.self) { self = .integer(x) return } if let x = try? container.decode(String.self) { self = .string(x) return } if let x = try? container.decode(ConfigDatumClass.self) { self = .configDatumClass(x) return } throw DecodingError.typeMismatch(ConfigDatumElement.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for ConfigDatumElement")) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .configDatumClass(let x): try container.encode(x) case .integer(let x): try container.encode(x) case .string(let x): try container.encode(x) } } } struct ConfigDatumClass: Codable { let name, configTitle: String enum CodingKeys: String, CodingKey { case name case configTitle = "config_title" } }使用enum很高兴,因为这样可以得到最大的类型安全性.其他答案似乎都没有.
It's nice to use the enum because you get the most type-safety that way. The other answers seem to lose this.
使用quicktype的便捷初始化程序选项,一个有效的代码示例为:
Using quicktype's convenience initializers option, a working code sample is:
let data = try ConfigData(""" { "config_data": [ { "name": "illuminate", "config_title": "Blink" }, { "name": "shoot", "config_title": "Fire" }, "illuminate", "shoot", 25, 100 ] } """) for item in data.configData { switch item { case .configDatumClass(let d): print("It's a class:", d) case .integer(let i): print("It's an int:", i) case .string(let s): print("It's a string:", s) } }此打印:
It's a class: ConfigDatumClass(name: "illuminate", configTitle: "Blink") It's a class: ConfigDatumClass(name: "shoot", configTitle: "Fire") It's a string: illuminate It's a string: shoot It's an int: 25 It's an int: 100更多推荐
Swift Codable
发布评论