如何将具有关联值的枚举与可编码协议一起使用?
How to use enum with assotiated values together with codable protocol?
我会解码以下 JSON,但它失败了。
{
"keyPath": [
"791186780675587"
],
"value": {
"name": "fff"
},
"operation": "setValue"
}
解码器也应该能够解码以下内容:
{
"keyPath": [
"791186780675587"
],
"value": "some_string",
"operation": "setValue"
}
区别在于 value
字段。
根元素的类型为 UpdateIn
struct UpdateIn: Content {
var keyPath: [String]
var value: Value
var operation: String
}
value
里面声明为enum
但是 enum
是如何声明的,但我不明白是什么。
enum Value: Codable {
case str(String)
case formField(FormField)
case passType(PassType)
case event(Event)
private enum CodingKeys: String, CodingKey {
case str, formField, passType, event
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .str(string):
try container.encode(string, forKey: .str)
case let .formField(field):
try container.encode(field, forKey: .formField)
case let .passType(field):
try container.encode(field, forKey: .passType)
case let .event(field):
try container.encode(field, forKey: .event)
}
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch container.allKeys.first {
case .str:
self = try .str(container.decode(String.self, forKey: .str))
case .formField:
self = try .formField(container.decode(FormField.self, forKey: .formField))
case .passType:
self = try .passType(container.decode(PassType.self, forKey: .passType))
case .event:
self = try .event(container.decode(Event.self, forKey: .event))
default:
throw DecodingError.dataCorrupted(
.init(
codingPath: container.codingPath,
debugDescription: "invalid data"
)
)
}
}
}
基本上 value
字段可以是 4 种不同的类型,简单的字符串,或事件,或其他两个。 (密码类型、表单字段)
case str(String)
case formField(FormField)
case passType(PassType)
case event(Event)
运行 解码会引发此错误:
invalid data for key value
因为在 switch
中调用了 default
案例,因为 container.allKeys
有 0 个项目,所以 switch container.allKeys.first
将跳转到 default
。
那么 Value
声明有什么问题?
啊是的,这就是 Event
结构的样子:
struct Event: Content {
var name: String
}
关键是您需要为 Value
自定义解码器,NOT 为 UpdateIn
!
假设 Event
是 decodable
。您首先尝试解码为第一种类型(在您的情况下为字符串),然后移至下一个和下一个。
enum Value {
case string(String)
case event(Event)
}
extension Value: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else {
try self = .event(container.decode(Event.self))
}
}
}
完整样本
let j1 = """
{
"keyPath": [
"791186780675587"
],
"value": {
"name": "fff"
},
"operation": "setValue"
}
""".data(using: .utf8)!
let j2 = """
{
"keyPath": [
"791186780675587"
],
"value": "simpleStrig",
"operation": "setValue"
}
""".data(using: .utf8)!
struct UpdateIn: Decodable {
var keyPath: [String]
var value: Value
var operation: String
}
enum Value {
case string(String)
case event(Event)
}
struct Event: Codable {
var name: String
}
extension Value: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else {
try self = .event(container.decode(Event.self))
}
}
}
let decoder = JSONDecoder()
print(try! decoder.decode(UpdateIn.self, from: j1).value)
print(try! decoder.decode(UpdateIn.self, from: j2).value)
我会解码以下 JSON,但它失败了。
{
"keyPath": [
"791186780675587"
],
"value": {
"name": "fff"
},
"operation": "setValue"
}
解码器也应该能够解码以下内容:
{
"keyPath": [
"791186780675587"
],
"value": "some_string",
"operation": "setValue"
}
区别在于 value
字段。
根元素的类型为 UpdateIn
struct UpdateIn: Content {
var keyPath: [String]
var value: Value
var operation: String
}
value
里面声明为enum
但是 enum
是如何声明的,但我不明白是什么。
enum Value: Codable {
case str(String)
case formField(FormField)
case passType(PassType)
case event(Event)
private enum CodingKeys: String, CodingKey {
case str, formField, passType, event
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .str(string):
try container.encode(string, forKey: .str)
case let .formField(field):
try container.encode(field, forKey: .formField)
case let .passType(field):
try container.encode(field, forKey: .passType)
case let .event(field):
try container.encode(field, forKey: .event)
}
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
switch container.allKeys.first {
case .str:
self = try .str(container.decode(String.self, forKey: .str))
case .formField:
self = try .formField(container.decode(FormField.self, forKey: .formField))
case .passType:
self = try .passType(container.decode(PassType.self, forKey: .passType))
case .event:
self = try .event(container.decode(Event.self, forKey: .event))
default:
throw DecodingError.dataCorrupted(
.init(
codingPath: container.codingPath,
debugDescription: "invalid data"
)
)
}
}
}
基本上 value
字段可以是 4 种不同的类型,简单的字符串,或事件,或其他两个。 (密码类型、表单字段)
case str(String)
case formField(FormField)
case passType(PassType)
case event(Event)
运行 解码会引发此错误:
invalid data for key value
因为在 switch
中调用了 default
案例,因为 container.allKeys
有 0 个项目,所以 switch container.allKeys.first
将跳转到 default
。
那么 Value
声明有什么问题?
啊是的,这就是 Event
结构的样子:
struct Event: Content {
var name: String
}
关键是您需要为 Value
自定义解码器,NOT 为 UpdateIn
!
假设 Event
是 decodable
。您首先尝试解码为第一种类型(在您的情况下为字符串),然后移至下一个和下一个。
enum Value {
case string(String)
case event(Event)
}
extension Value: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else {
try self = .event(container.decode(Event.self))
}
}
}
完整样本
let j1 = """
{
"keyPath": [
"791186780675587"
],
"value": {
"name": "fff"
},
"operation": "setValue"
}
""".data(using: .utf8)!
let j2 = """
{
"keyPath": [
"791186780675587"
],
"value": "simpleStrig",
"operation": "setValue"
}
""".data(using: .utf8)!
struct UpdateIn: Decodable {
var keyPath: [String]
var value: Value
var operation: String
}
enum Value {
case string(String)
case event(Event)
}
struct Event: Codable {
var name: String
}
extension Value: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let string = try? container.decode(String.self) {
self = .string(string)
} else {
try self = .event(container.decode(Event.self))
}
}
}
let decoder = JSONDecoder()
print(try! decoder.decode(UpdateIn.self, from: j1).value)
print(try! decoder.decode(UpdateIn.self, from: j2).value)