Partage via


Remplir des propriétés initialisées

À compter de .NET 8, vous pouvez spécifier une préférence pour remplacer ou remplir les propriétés .NET lorsque JSON est désérialisé. L’énumération JsonObjectCreationHandling fournit les choix de gestion des créations d’objets :

Comportement par défaut (remplacer)

Le désérialiseur System.Text.Json crée toujours une nouvelle instance du type cible. Toutefois, même si une nouvelle instance est créée, certaines propriétés et champs peuvent déjà être initialisés dans le cadre de la construction d’objet. Considérez le type de suivant :

class A
{
    public List<int> Numbers1 { get; } = [1, 2, 3];
    public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

Lorsque vous créez une instance de cette classe, la valeur de propriété Numbers1 (et Numbers2) est une liste avec trois éléments (1, 2 et 3). Si vous désérialisez JSON à ce type, le comportement par défaut est que les valeurs de propriété sont remplacées :

  • Pour Numbers1, étant donné qu’il est en lecture seule (aucun setter), il a toujours les valeurs 1, 2 et 3 dans sa liste.
  • Pour Numbers2, qui est en lecture-écriture, une nouvelle liste est allouée et les valeurs à partir du JSON sont ajoutées.

Par exemple, si vous exécutez le code de désérialisation suivant, Numbers1 contient les valeurs 1, 2 et 3 et Numbers2 contient les valeurs 4, 5 et 6.

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");

Remplir la propriété comportement

À compter de .NET 8, vous pouvez modifier le comportement de désérialisation pour modifier (remplir) les propriétés et les champs au lieu de les remplacer :

  • Pour une propriété de type de collection, l’objet est réutilisé sans effacer. Si la collection est préremplie avec des éléments, elle s’affiche dans le résultat désérialisé final, ainsi que les valeurs du JSON. Pour obtenir un exemple, consultez Exemple de propriété Collection.

  • Pour une propriété qui est un objet avec des propriétés, ses propriétés mutables sont mises à jour vers les valeurs JSON, mais la référence d’objet elle-même ne change pas.

  • Pour une propriété de type de struct, le comportement efficace est que pour ses propriétés mutables, toutes les valeurs existantes sont conservées et de nouvelles valeurs à partir du JSON sont ajoutées. Toutefois, contrairement à une propriété de référence, l’objet lui-même n’est pas réutilisé, car il s’agit d’un type valeur. Au lieu de cela, une copie du struct est modifiée, puis réaffectée à la propriété. Pour obtenir un exemple, consultez Exemple de propriété Struct.

    Une propriété de struct doit avoir un setter ; sinon, une InvalidOperationException est levée au moment de l’exécution.

Remarque

Le comportement de remplissage ne fonctionne actuellement pas pour les types qui ont un constructeur paramétrisé. Pour obtenir plus d’informations, consultez Problème dotnet/runtime 92877.

Propriétés en lecture seule

Pour remplir les propriétés de référence qui sont mutables, étant donné que l’instance des références de propriété n’est pas remplacée, la propriété n’a pas besoin d’avoir un setter. Ce comportement signifie que la désérialisation peut également remplir les propriétés en lecture seule.

Remarque

Les propriétés de struct nécessitent toujours des setters, car l’instance est remplacée par une copie modifiée.

Exemple de propriété collection

Considérez la même classe A de l’exemple de remplacement du comportement, mais cette fois annotée avec une préférence pour remplir les propriétés au lieu de les remplacer :

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class A
{
    public List<int> Numbers1 { get; } = [1, 2, 3];
    public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

Si vous exécutez le code de désérialisation suivant, Numbers1 et Numbers2 contiennent les valeurs 1, 2, 3, 4, 5 et 6 :

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");

Exemple de propriété Struct

La classe suivante contient une propriété struct, S1, dont le comportement de désérialisation est défini sur Populate. Après avoir exécuté ce code, c.S1.Value1 a la valeur 10 (à partir du constructeur) et c.S1.Value2 a la valeur 5 (à partir du JSON).

C? c = JsonSerializer.Deserialize<C>("""{"S1": {"Value2": 5}}""");

class C
{
    public C()
    {
        _s1 = new S
        {
            Value1 = 10
        };
    }

    private S _s1;

    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    public S S1
    {
        get { return _s1; }
        set { _s1 = value; }
    }
}

struct S
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

Si le comportement par défaut Replace a été utilisé à la place, la valeur par défaut de c.S1.Value1 serait 0 après la désérialisation. C’est parce que le constructeur C() serait appelé, affectant la valeur de c.S1.Value1 sur 10, mais la valeur de S1 serait remplacée par une nouvelle instance. (c.S1.Value2 serait toujours 5, étant donné que le JSON remplace la valeur par défaut.)

Comment spécifier

Il existe plusieurs façons de spécifier une préférence pour Remplacer ou Remplir :

  • Utilisez l’attribut JsonObjectCreationHandlingAttribute pour annoter au niveau du type ou de la propriété. Si vous définissez l’attribut au niveau du type et définissez sa propriété Handling sur Populate, le comportement s’applique uniquement à ces propriétés où la population est possible (par exemple, les types valeur doivent avoir un setter).

    Si vous souhaitez que la préférence à l’échelle du type soit Populate, mais que vous souhaitez exclure une ou plusieurs propriétés de ce comportement, vous pouvez ajouter l’attribut au niveau du type et à nouveau au niveau de la propriété pour remplacer le comportement hérité. Cela est illustré par l'exemple de code suivant.

    // Type-level preference is Populate.
    [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
    class B
    {
        // For this property only, use Replace behavior.
        [JsonObjectCreationHandling(JsonObjectCreationHandling.Replace)]
        public List<int> Numbers1 { get; } = [1, 2, 3];
        public List<int> Numbers2 { get; set; } = [1, 2, 3];
    }
    
  • Définissez JsonSerializerOptions.PreferredObjectCreationHandling (ou JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling, pour la génération source) pour spécifier une préférence globale.

    var options = new JsonSerializerOptions
    {
        PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate
    };