Lorsque j'utilise des types d'enregistrement pour désérialiser JSON, comment puis-je éviter les valeurs nulles pour les membres manquants ?
J'utilise Newtonsoft Json pour désérialiser JSON en types d'enregistrement C#. Je vais donner des exemples ci-dessous, mais le thème du défi ici est d'essayer d'obtenir l'empreinte et la sémantique de l'objet dans le code que j'aime, tout en obtenant le comportement de désérialisation que je veux. Essentiellement, j'aime éviter null
autant que possible les valeurs à l'intérieur du code de désérialisation. Cela s'applique principalement aux string
types de collection et: Si une propriété de chaîne est manquante, je ne veux pas null
, je veux ""
. De même, pour les propriétés de collection manquantes, je ne veux null
qu'un conteneur vide (par exemple Array.Empty<MyComplexType>()
).
La façon dont j'avais l'habitude d'éviter cela avant record
les types est de spécifier explicitement les propriétés par défaut, comme ceci:
public class ReleaseProfileData
{
IReadOnlyCollection<TermData> Required { get; set; } = Array.Empty<TermData>();
}
Je ne peux pas le faire lorsque je le convertis en équivalent de type d'enregistrement abrégé :
public record ReleaseProfileData(
IReadOnlyCollection<TermData> Required = Array.Empty<TermData>()
);
J'ai donc opté pour ceci à la place:
public record ReleaseProfileData(
IReadOnlyCollection<TermData> Required
);
Cependant, si dans mes données JSON il me manque la "required": []
propriété, le Required
paramètre ici sera null
. Je veux plutôt que ce soit un conteneur vide.
J'ai considéré deux objets et fait une conversion entre eux, mais cela semble exagéré. J'ai également examiné les paramètres de NewtonsoftJson, tels que MissingMemberHandling
, NullValueHandling
, etc. Mais aucun de ceux-ci ne semble simplement dire : "Si une propriété est manquante, donnez-moi une chaîne ou un conteneur vide au lieu de null
".
Je me rends compte que je peux faire une version longue de mon record
type en utilisant init
des propriétés, mais encore une fois, cela commence à devenir très verbeux : l'initialisation se fait maintenant via le style brace-init, au lieu du constructeur. Je peux ajouter un constructeur, mais encore une fois, cela le rend plus verbeux. Et en plus de cela, je dois maintenant implémenter des Destruction
méthodes si je veux diviser mon objet en var (first, second, etc)
. Cela ne fait qu'empirer.
Quelle est la bonne approche ici ?
Solution du problème
Vous pouvez définir votre propre propriété avec le même nom pour "modifier" la propriété positionnelle générée de l'enregistrement. De la doc:
Si la définition de propriété implémentée automatiquement générée ne vous convient pas, vous pouvez définir votre propre propriété du même nom. Par exemple, vous souhaiterez peut-être modifier l'accessibilité ou la mutabilité, ou fournir une implémentation pour l' accesseur get
ou. set
Si vous déclarez la propriété dans votre source, vous devez l'initialiser à partir du paramètre positionnel de l'enregistrement. Si votre propriété est une propriété mise en œuvre automatiquement, vous devez initialiser la propriété.
public record ReleaseProfileData(IReadOnlyCollection<object> Required)
{
public IReadOnlyCollection<object> Required { get; init; } = Required?? Array.Empty<object>();
}
Commentaires
Enregistrer un commentaire