Codable 解码 属性 具有基于另一个值的多个对象类型
Codable decode property with multiple object types BASED on another value
这与解码具有多种类型(int、字符串)的 属性 值无关。
我有一个名为 data
的对象,它可以 return 多种类型,此时可以做的事情可能看起来像这样:
enum MyData: Codable {
case ObjOne(groupObject)
case ObjTwo(imageObject)
init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer()
if let v = try? value.decode(groupObject.self) {
self = .ObjOne(v)
return
} else if let v = try? value.decode(imageObject.self) {
self = .ObjTwo(v)
return
}
throw Rating.ParseError.notRecognizedType(value)
}
enum ParseError: Error {
case notRecognizedType(Any)
}
}
这里的问题是我尝试根据之前解码过程中使用的另一个值来使 MyData
解码对象,简而言之,我想将一个值传递给 MyData
这样它就可以确定解码哪个
我有这个
enum ContentType: String, Codable {
case linear
case grid
case slider
}
而且我希望 MyData
了解这个 ContentType
值,以便 MyData
可以确定流程的走向,
那么 ContentType 是从哪里来的呢?它在前一个主要对象的相同属性列表中,来自看起来像这样的东西
struct Catalog: Codable {
var dataType: ContentType?
var data: MyData?
}
我想用更简单的话实现什么?
struct Catalog: Codable {
var dataType: ContentType?
var data: MyData<dataType>? <--// i know this is not possible,
// -- but i want MyData to know about the dataType value that will be decoded
}
---------- JSON 我要解析
[{
"data_type": "group",
"data": {
"group_id": 127 // this refers to object : groupObject
}
},
{
"data_type": "image",
"data": {
"image": "http://google.com/favicon" // this is referring : imageObject
}
}
]
你看上面的一点,就是"data"可以return不同的对象,根据data_type[=22的值=]
我没有使用泛型,而是创建了一个符合 Decodable
的空协议,并将其用作 data
的类型。然后内容结构需要符合这个协议。
protocol MyData: Decodable {}
struct Group: MyData {
let groupId: Int
}
struct Image: MyData {
let image: String
}
struct Catalog: Decodable {
var dataType: String
var data: MyData
enum CodingKeys: String, CodingKey {
case dataType, data
}
enum ParseError: Error {
case notRecognizedType(Any)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dataType = try container.decode(String.self, forKey: .dataType)
switch dataType {
case "group":
data = try container.decode(Group.self, forKey: .data)
case "image":
data = try container.decode(Image.self, forKey: .data)
default:
throw ParseError.notRecognizedType(dataType)
}
}
}
请注意,我没有在 init
中使用枚举 ContentType
,因为它与样本 json 数据不匹配,但应该很容易修复。
使用此解决方案的标准代码
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([Catalog].self, from: data)
print(result)
} catch {
print(error)
}
这与解码具有多种类型(int、字符串)的 属性 值无关。
我有一个名为 data
的对象,它可以 return 多种类型,此时可以做的事情可能看起来像这样:
enum MyData: Codable {
case ObjOne(groupObject)
case ObjTwo(imageObject)
init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer()
if let v = try? value.decode(groupObject.self) {
self = .ObjOne(v)
return
} else if let v = try? value.decode(imageObject.self) {
self = .ObjTwo(v)
return
}
throw Rating.ParseError.notRecognizedType(value)
}
enum ParseError: Error {
case notRecognizedType(Any)
}
}
这里的问题是我尝试根据之前解码过程中使用的另一个值来使 MyData
解码对象,简而言之,我想将一个值传递给 MyData
这样它就可以确定解码哪个
我有这个
enum ContentType: String, Codable {
case linear
case grid
case slider
}
而且我希望 MyData
了解这个 ContentType
值,以便 MyData
可以确定流程的走向,
那么 ContentType 是从哪里来的呢?它在前一个主要对象的相同属性列表中,来自看起来像这样的东西
struct Catalog: Codable {
var dataType: ContentType?
var data: MyData?
}
我想用更简单的话实现什么?
struct Catalog: Codable {
var dataType: ContentType?
var data: MyData<dataType>? <--// i know this is not possible,
// -- but i want MyData to know about the dataType value that will be decoded
}
---------- JSON 我要解析
[{
"data_type": "group",
"data": {
"group_id": 127 // this refers to object : groupObject
}
},
{
"data_type": "image",
"data": {
"image": "http://google.com/favicon" // this is referring : imageObject
}
}
]
你看上面的一点,就是"data"可以return不同的对象,根据data_type[=22的值=]
我没有使用泛型,而是创建了一个符合 Decodable
的空协议,并将其用作 data
的类型。然后内容结构需要符合这个协议。
protocol MyData: Decodable {}
struct Group: MyData {
let groupId: Int
}
struct Image: MyData {
let image: String
}
struct Catalog: Decodable {
var dataType: String
var data: MyData
enum CodingKeys: String, CodingKey {
case dataType, data
}
enum ParseError: Error {
case notRecognizedType(Any)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dataType = try container.decode(String.self, forKey: .dataType)
switch dataType {
case "group":
data = try container.decode(Group.self, forKey: .data)
case "image":
data = try container.decode(Image.self, forKey: .data)
default:
throw ParseError.notRecognizedType(dataType)
}
}
}
请注意,我没有在 init
中使用枚举 ContentType
,因为它与样本 json 数据不匹配,但应该很容易修复。
使用此解决方案的标准代码
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([Catalog].self, from: data)
print(result)
} catch {
print(error)
}