这是我正在尝试解码的JSON。objectType
的值决定了要创建的对象。
{
"options": [
{
"objectType": "OptionTypeA",
"label": "optionALabel1",
"value": "optionAValue1"
},
{
"objectType": "OptionTypeB",
"label": "optionBLabel",
"value": "optionBValue"
},
{
"objectType": "OptionTypeA",
"label": "optionALabel2",
"value": "optionAValue2"
}
]
}
假设我有这样定义的 2 种选项类型
public protocol OptionType {
var label: String { get }
var value: String { get }
}
struct OptionTypeA: Decodable {
let label: String
let value: String
// and some others...
}
struct OptionTypeB: Decodable {
let label: String
let value: String
// and some others...
}
struct Option: Decodable {
let options: [OptionType]
enum CodingKeys: String, CodingKey {
case options
case label
case value
case objectType
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
var optionsContainer = try values.nestedUnkeyedContainer(forKey: .options)
var options = [OptionType]()
while !optionsContainer.isAtEnd {
let itemContainer = try optionsContainer.nestedContainer(keyedBy: CodingKeys.self)
switch try itemContainer.decode(String.self, forKey: .objectType) {
// What should I do here so that I do not have to manually decode `OptionTypeA` and `OptionTypeB`?
case "OptionTypeA": options.append()
case "OptionTypeB": options.append()
default: fatalError("Unknown type")
}
}
self.options = options
}
}
我知道我可以在itemContainer
中手动解码每个键,并在开关盒中创建单独的选项类型对象。但我不想那样做。如何解码这些对象?
比通用属性协议更快捷的方法是具有关联值的枚举。
Option
枚举首先解码对象类型 - 甚至可以解码为枚举 - 并根据值解码不同的结构。
enum OptionType : String, Decodable {
case a = "OptionTypeA", b = "OptionTypeB"
}
struct Root : Decodable {
let options : [Option]
}
enum Option : Decodable {
private enum CodingKeys : String, CodingKey { case objectType }
case typeA(OptionTypeA)
case typeB(OptionTypeB)
init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let typeContainer = try decoder.singleValueContainer()
let optionType = try container.decode(OptionType.self, forKey: .objectType)
switch optionType {
case .a: self = .typeA(try typeContainer.decode(OptionTypeA.self))
case .b: self = .typeB(try typeContainer.decode(OptionTypeB.self))
}
}
}
struct OptionTypeA: Decodable {
let label: String
let value: String
// and some others...
}
struct OptionTypeB: Decodable {
let label: String
let value: String
// and some others...
}
<小时 />let jsonString = """
{
"options": [
{
"objectType": "OptionTypeA",
"label": "optionALabel1",
"value": "optionAValue1"
},
{
"objectType": "OptionTypeB",
"label": "optionBLabel",
"value": "optionBValue"
},
{
"objectType": "OptionTypeA",
"label": "optionALabel2",
"value": "optionAValue2"
}
]
}
"""
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Root.self, from: data)
for option in result.options {
switch option {
case .typeA(let optionTypeA): print(optionTypeA)
case .typeB(let optionTypeB): print(optionTypeB)
}
}
} catch {
print(error)
}