使用 Swift 解码解码异构数组 JSON



这是我正在尝试解码的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)
}

相关内容

  • 没有找到相关文章

最新更新