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 nullautant que possible les valeurs à l'intérieur du code de désérialisation. Cela s'applique principalement aux stringtypes 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 nullqu'un conteneur vide (par exemple Array.Empty<MyComplexType>()).

La façon dont j'avais l'habitude d'éviter cela avant recordles 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 Requiredparamè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 recordtype en utilisant initdes 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 Destructionmé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 getou. setSi 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

Posts les plus consultés de ce blog

Erreur Symfony : "Une exception a été levée lors du rendu d'un modèle"

Détecter les appuis sur les touches fléchées en JavaScript

Une chaîne vide donne "Des erreurs ont été détectées dans les arguments de la ligne de commande, veuillez vous assurer que tous les arguments sont correctement définis"