2017-10-14 10 views
1

(Ceci est un suivi de cette question. Using Decodable protocol with multiples keys)Swift décodables en option Clé

je le code suivant Swift:

let additionalInfo = try values.nestedContainer(keyedBy: UserInfoKeys.self, forKey: .age) 
age = try additionalInfo.decodeIfPresent(Int.self, forKey: .realage) 

Je sais que si j'utilise decodeIfPresent et don N'a pas la propriété, il va encore gérer correctement si c'est une variable optionnelle. Par exemple, le fichier JSON suivant permet de l'analyser en utilisant le code ci-dessus.

{ 
    "firstname": "Test", 
    "lastname": "User", 
    "age": {"realage": 29} 
} 

Et le JSON suivant fonctionne également.

{ 
    "firstname": "Test", 
    "lastname": "User", 
    "age": {"notrealage": 30} 
} 

Mais ce qui suit ne fonctionne pas.

{ 
    "firstname": "Test", 
    "lastname": "User" 
} 

Comment puis-je faire fonctionner les 3 exemples? Y at-il quelque chose de similaire à decodeIfPresent pour nestedContainer?

Répondre

8

Vous pouvez utiliser la fonction KeyedDecodingContainer suivante:

func contains(_ key: KeyedDecodingContainer.Key) -> Bool 

Renvoie une valeur Bool indiquant si le décodeur contient une valeur associée à la clé donnée. La valeur associée à la clé donnée peut être une valeur nulle appropriée pour le format de données.

pour vérifier si la clé existe "age"avant demandant le conteneur imbriqué correspondant. Par exemple:

struct Person: Decodable { 
    let firstName, lastName: String 
    let age: Int? 

    enum CodingKeys: String, CodingKey { 
     case firstName = "firstname" 
     case lastName = "lastname" 
     case age 
    } 

    enum AgeKeys: String, CodingKey { 
     case realAge = "realage" 
     case fakeAge = "fakeage" 
    } 

    init(from decoder: Decoder) throws { 
     let values = try decoder.container(keyedBy: CodingKeys.self) 
     self.firstName = try values.decode(String.self, forKey: .firstName) 
     self.lastName = try values.decode(String.self, forKey: .lastName) 

     if values.contains(.age) { 
      let age = try values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age) 
      self.age = try age.decodeIfPresent(Int.self, forKey: .realAge) 
     } else { 
      self.age = nil 
     } 
    } 
} 
0

Pouvez-vous essayer de coller votre exemple JSON dans quicktype pour voir quels types il déduit? Sur la base de votre question, je collais vos échantillons et obtenu:

struct UserInfo: Codable { 
    let firstname: String 
    let age: Age? 
    let lastname: String 
} 

struct Age: Codable { 
    let realage: Int? 
} 

Faire UserInfo.age et Age.realage optionals fonctionne, si c'est ce que vous essayez d'accomplir.

+1

J'essaye de réaliser ceci avec 1 classe/struct. J'ai des raisons spécifiques pour le concevoir avec 1 classe/struct. Voir ma question et répondre [ici] (https://stackoverflow.com/a/46737735/894067) pour plus d'informations. –

+0

Ah, je vois - je suis d'accord avec la réponse qui suggère un modèle «brut» correspondant à la structure JSON, que vous pourriez mapper à votre modèle de niveau supérieur avec le comportement idiosyncratique que vous désirez! Je pense que cela pourrait être beaucoup plus facile à maintenir et à comprendre que la mise en œuvre de décodeurs personnalisés, mais bonne chance de toute façon! –

+0

La façon dont mes données sont structurées dans mes fichiers serveur simplifie l'utilisation des décodeurs personnalisés. Et juste de ma compréhension et de ma structuration, c'est beaucoup plus facile de le faire de cette façon. Peut-être que quelqu'un sera capable de donner une réponse qui correspond à ce que je veux. Merci pour votre aide. –