Swift Decodable - come evitare il tipo generico?
Sto recuperando un oggetto nidificato complesso dalla mia 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
Suppongo che il modo corretto per affrontare questo problema sia introdurre un tipo generico.
struct Value<V: Decodable>: Decodable {
let key: String
let val: V?
}
... ma anche così, values
potrebbe essere un array misto, quindi non vedo come V
sarebbe utile dichiarare ciò che è.
Ma poi, ovviamente, il tipo generico si propaga lungo la gerarchia, DocumentDraft
all'oggetto, all'editore, alle mie chiamate API, ecc. Inquinando l'intera catena di chiamate e oggetti altrimenti molto puliti e leggibili. Vorrei occuparmi di questo solo a livello di Value
, e lasciare che JSONDecoder restituisca semplicemente uno dei due in qualche modo.
C'è un altro modo per trattare le due possibilità dell'opzionale val
come uno String
o [String]
senza cambiare l'intero oggetto genitore?
Risposte
Puoi ottenerlo utilizzando solo il [String]
tipo e implementando manualmente la init(from:)
funzione del Decodable
protocollo in questo modo:
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 la decodifica in String
valore ha esito positivo, creare un array di stringhe con un solo elemento. Quando la decodifica in String
valore non riesce, prova a decodificare come[String]