如何将具有关联值的枚举与可编码协议一起使用?

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 自定义解码器,NOTUpdateIn!

假设 Eventdecodable。您首先尝试解码为第一种类型(在您的情况下为字符串),然后移至下一个和下一个。

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)