Swift Decodable - como evitar o tipo genérico?

Aug 19 2020

Estou recuperando um objeto aninhado complexo de minha API JSON REST.

DocumentDraft
 - uuid: String
 - schema: Schema // Very complicated object with many variations
 - url: String
 - values: [Value]
 - successors: [String]
 - predecessors: [String]

Value
 - key: String
 - val: String? OR [String]?   // <-- This is the problem

Suponho que a maneira adequada de lidar com isso seja introduzir um tipo genérico.

struct Value<V: Decodable>: Decodable {
  let key: String
  let val: V?
}

... mas mesmo assim, valuespoderia ser uma matriz mista, então não vejo como declarar o Vque ajudaria.

Mas então, é claro que o tipo genérico se propaga por toda a hierarquia, para o DocumentDraftobjeto, para o editor, para minhas chamadas de API, etc. poluindo toda a cadeia de chamadas e objetos de outra forma muito limpos e legíveis. Eu gostaria de lidar com isso apenas no nível de Valuee deixar o JSONDecoder simplesmente retornar um dos dois de alguma forma.

Existe outra maneira de lidar com as duas possibilidades do opcional valcomo um Stringou outro [String]sem alterar todo o objeto pai?

Respostas

4 gcharita Aug 19 2020 at 12:31

Você pode conseguir isso usando apenas o [String]tipo e implementando manualmente a init(from:)função do Decodableprotocolo como este:

struct Value: Decodable {
    let key: String
    let val: [String]?
    
    enum CodingKeys: String, CodingKey {
        case key, val
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        key = try container.decode(String.self, forKey: .key)
        do {
            if let string = try container.decodeIfPresent(String.self, forKey: .val) {
                val = [string]
            } else {
                val = nil
            }
        } catch DecodingError.typeMismatch {
            val = try container.decodeIfPresent([String].self, forKey: .val)
        }
    }
}

Quando a decodificação para Stringvalor for bem-sucedida, crie uma matriz de strings com apenas um elemento. Quando a decodificação para o Stringvalor falha, tente decodificar como[String]