Swift Decodable - comment éviter le type générique?

Aug 19 2020

Je récupère un objet imbriqué complexe à partir de mon 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

Je suppose que la bonne façon de traiter ce problème est d'introduire un type générique.

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

... mais même ainsi, valuespourrait être un tableau mixte, donc je ne vois pas comment déclarer ce que Vc'est aiderait.

Mais alors, bien sûr, le type générique se propage tout en haut de la hiérarchie, à l' DocumentDraftobjet, à l'éditeur, à mes appels API, etc. polluant toute la chaîne d'appels et d'objets autrement très propres et lisibles. Je voudrais traiter de cela uniquement au niveau de Value, et laisser le JSONDecoder simplement renvoyer l'un des deux d'une manière ou d'une autre.

Existe-t-il une autre façon de traiter les deux possibilités de l'option valcomme l'un Stringou l' autre ou [String]sans modifier l'ensemble de l'objet parent?

Réponses

4 gcharita Aug 19 2020 at 12:31

Vous pouvez y parvenir en utilisant uniquement le [String]type et en implémentant manuellement la init(from:)fonction de Decodableprotocole comme ceci:

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)
        }
    }
}

Lorsque le décodage en Stringvaleur réussit, créez un tableau de chaînes avec un seul élément. Lorsque le décodage en Stringvaleur échoue, essayez de décoder comme[String]