Декодирование неизвестных кодируемых значений перечисления по умолчанию


Я должен десериализовать строку JSON следующим образом:

{ "name" : "John Smith", "value" : "someValue" }

В Swift 4, где "значение" должно быть перечислением, а весь объект-структурой, подобной:

struct MyType {
    name: String?
    value: Value?
}

В какой-то момент в будущем, возможно, будут добавлены новые значения enum в бэкэнде, поэтому я подумал, что было бы разумно иметь некоторый запасной вариант.

Я думал, что смогу создать перечисление, подобное

enum Value {
    case someValue
    case someOtherValue
    case unknown(value: String)
}
Но я просто не могу понять, как десериализовать это перечисление и заставить его работать. Ранее я просто использовал перечисление String , но десериализация неизвестных значений приводит к ошибкам.

Есть ли простой способ заставить это работать или я должен десериализовать значение как String и создать пользовательский геттер в структуре с оператором switch, чтобы вернуть один из случаев (вероятно, даже не в самой структуре, а в моей модели представления)?

1 3

1 ответ:

Можно реализовать init(from decoder: Decoder) и encode(to encoder: Encoder) и обрабатывать каждый случай явно, т. е.

struct MyType: Codable
{
    var name: String?
    var value: Value?

    enum CodingKeys: String, CodingKey
    {
        case name
        case value
    }

    init(from decoder: Decoder) throws
    {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        name = try values.decode(String.self, forKey: .name)
        let strValue = try values.decode(String.self, forKey: .value)
        //You need to handle every case explicitly
        switch strValue
        {
        case "someValue":
            value = Value.someValue
        case "someOtherValue":
            value = Value.someOtherValue
        default:
            value = Value.unknown(value: strValue)
        }
    }

    func encode(to encoder: Encoder) throws
    {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        if let val = value
        {
            //You need to handle every case explicitly
            switch val
            {
            case .someValue, .someOtherValue:
                try container.encode(String(describing: val), forKey: .value)
            case .unknown(let strValue):
                try container.encode(strValue, forKey: .value)
            }
        }
    }
}

enum Value
{
    case someValue
    case someOtherValue
    case unknown(value: String)
}