Coder la valeur nulle en tant que null avec JSONEncoder

J'utilise JSONEncoder Swift 4. J'ai une structure Codable avec une propriété optionnelle, et j'aimerais que cette propriété apparaisse comme valeur null dans datatables JSON produites quand la valeur est nil . Toutefois, JSONEncoder ignore la propriété et ne l'ajoute pas à la sortie JSON. Est-il possible de configurer JSONEncoder afin qu'il conserve la key et la définit à null dans ce cas?

Exemple

L'extrait de code ci-dessous produit {"number":1} , mais je préfère qu'il me donne {"ssortingng":null,"number":1} :

 struct Foo: Codable { var ssortingng: Ssortingng? = nil var number: Int = 1 } let encoder = JSONEncoder() let data = try! encoder.encode(Foo()) print(Ssortingng(data: data, encoding: .utf8)!) 

Oui, mais vous devrez écrire votre propre encodeur; vous ne pouvez pas utiliser celui par défaut.

 struct Foo: Codable { var ssortingng: Ssortingng? = nil var number: Int = 1 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(number, forKey: .number) try container.encode(ssortingng, forKey: .ssortingng) } } 

Encoder un optionnel directement encodera un null, comme vous le souhaitez.

Si c'est un cas d'utilisation important pour vous, vous pouvez envisager d'ouvrir un défaut sur bugs.swift.org pour requestr l' ajout d'un nouvel indicateur OptionalEncodingStrategy sur JSONEncoder pour correspondre à la DateEncodingStrategy existante, etc. (Voir ci-dessous pourquoi cela est probablement impossible pour implémenter réellement dans Swift aujourd'hui, mais entrer dans le système de suivi est toujours utile pendant que Swift évolue.)


Edit: Pour les questions de Paulo ci-dessous, ceci est envoyé à la version générique encode<T: Encodable> car Optional est conforme à Encodable . Ceci est implémenté dans Codable.swift de cette façon:

 extension Optional : Encodable /* where Wrapped : Encodable */ { @_inlineable // FIXME(sil-serialize-all) public func encode(to encoder: Encoder) throws { assertTypeIsEncodable(Wrapped.self, in: type(of: self)) var container = encoder.singleValueContainer() switch self { case .none: try container.encodeNil() case .some(let wrapped): try (wrapped as! Encodable).__encode(to: &container) } } } 

Cela encodeNil l'appel à encodeNil , et je pense que le fait de laisser stdlib gérer les Optionals comme un simple encodeur est préférable à un traitement spécial dans notre propre encodeur et à l'appel de encodeNil .

Une autre question évidente est pourquoi cela fonctionne de cette façon en premier lieu. Puisque Optional est Encodable, et que la conformité Encodable générée code toutes les propriétés, pourquoi "encoder toutes les propriétés à la main" fonctionne-t-il différemment? La réponse est que le générateur de conformité inclut un cas spécial pour les optionnels :

 // Now need to generate `try container.encode(x, forKey: .x)` for all // existing properties. Optional properties get `encodeIfPresent`. ... if (varType->getAnyNominal() == C.getOptionalDecl() || varType->getAnyNominal() == C.getImplicitlyUnwrappedOptionalDecl()) { methodName = C.Id_encodeIfPresent; } 

Cela signifie que changer ce comportement nécessiterait de changer la conformité générée automatiquement, pas JSONEncoder (ce qui signifie également qu'il est probablement très difficile à configurer dans Swift d'aujourd'hui ….)