Swift Decodable: ¿cómo evitar el tipo genérico?
Estoy recuperando un objeto anidado complejo de mi API REST JSON.
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
Supongo que la forma correcta de lidiar con esto es introducir un tipo genérico.
struct Value<V: Decodable>: Decodable {
let key: String
let val: V?
}
... pero aún así, values
podría ser una matriz mixta, por lo que no veo cómo declarar lo que V
es ayudaría.
Pero luego, por supuesto, el tipo genérico se propaga hacia arriba en la jerarquía, al DocumentDraft
objeto, al editor, a mis llamadas a la API, etc. contaminando toda la cadena de llamadas y objetos que de otro modo serían muy limpios y legibles. Me gustaría lidiar con esto solo en el nivel de Value
, y dejar que JSONDecoder simplemente devuelva uno de los dos de alguna manera.
¿Hay otra forma de tratar las dos posibilidades del opcional val
como una String
o [String]
sin cambiar todo el objeto principal?
Respuestas
Puede lograrlo usando solo el [String]
tipo e implementando manualmente la init(from:)
función del Decodable
protocolo 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)
}
}
}
Cuando la decodificación a String
valor tenga éxito, cree una matriz de cadenas con un solo elemento. Cuando la decodificación a String
valor falla, intente decodificar como[String]