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ì, valuespotrebbe essere un array misto, quindi non vedo come Vsarebbe utile dichiarare ciò che è.
Ma poi, ovviamente, il tipo generico si propaga lungo la gerarchia, DocumentDraftall'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 valcome uno Stringo [String]senza cambiare l'intero oggetto genitore?
Risposte
Puoi ottenerlo utilizzando solo il [String]tipo e implementando manualmente la init(from:)funzione del Decodableprotocollo 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 Stringvalore ha esito positivo, creare un array di stringhe con un solo elemento. Quando la decodifica in Stringvalore non riesce, prova a decodificare come[String]