Sdílet prostřednictvím


Naplnění inicializovaných vlastností

Počínaje rozhraním .NET 8 můžete zadat předvolbu nahrazení nebo naplnění vlastností .NET při deserializaci JSON. Výčet JsonObjectCreationHandling poskytuje možnosti zpracování vytváření objektů:

Výchozí chování (nahrazení)

System.Text.Json Deserializátor vždy vytvoří novou instanci cílového typu. I když je však vytvořena nová instance, některé vlastnosti a pole již mohou být inicializovány jako součást konstrukce objektu. Zvažte následující typ:

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

Při vytváření instance této třídy Numbers1 je hodnota vlastnosti (a Numbers2) seznam se třemi prvky (1, 2 a 3). Pokud deserializujete JSON na tento typ, výchozí chování je, že hodnoty vlastností jsou nahrazeny:

  • Pro Numbers1, protože je jen pro čtení (bez setter), má stále hodnoty 1, 2 a 3 v seznamu.
  • Pro Numbers2, který je pro čtení i zápis, je přidělen nový seznam a hodnoty z JSON se přidají.

Pokud například spustíte následující deserializační kód, Numbers1 obsahuje hodnoty 1, 2 a 3 a Numbers2 obsahuje hodnoty 4, 5 a 6.

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

Naplnění chování

Počínaje rozhraním .NET 8 můžete změnit chování deserializace a upravit (naplnit) vlastnosti a pole namísto jejich nahrazení:

  • U vlastnosti typu kolekce se objekt znovu použije bez vymazání. Pokud je kolekce předem vyplněná prvky, zobrazí se v konečném deserializovaném výsledku spolu s hodnotami z JSON. Příklad viz Příklad vlastnosti Kolekce.

  • U vlastnosti, která je objektem s vlastnostmi, se jeho proměnlivé vlastnosti aktualizují na hodnoty JSON, ale samotný odkaz na objekt se nezmění.

  • U vlastnosti typu struktury je efektivní chování, že pro jeho proměnlivé vlastnosti jsou všechny existující hodnoty zachovány a přidány nové hodnoty z JSON. Na rozdíl od vlastnosti odkazu se však samotný objekt znovu nepoužívá, protože se jedná o typ hodnoty. Místo toho je změněna kopie struktury a potom znovu přiřazena k vlastnosti. Příklad viz Příklad vlastnosti Struktury.

    Vlastnost struktury musí mít setter; InvalidOperationException v opačném případě je vyvolán za běhu.

Poznámka:

Chování naplnění v současné době nefunguje u typů, které mají parametrizovaný konstruktor. Další informace najdete v tématu dotnet/runtime problém 92877.

Vlastnosti jen pro čtení

Pro naplnění vlastností odkazu, které jsou proměnlivé, protože instance, na kterou odkazy na vlastnosti nejsou nahrazeny, nemusí mít vlastnost setter. Toto chování znamená, že deserializace může také naplnit vlastnosti jen pro čtení.

Poznámka:

Vlastnosti struktury stále vyžadují setters, protože instance je nahrazena změněnou kopií.

Příklad vlastnosti kolekce

Vezměte v úvahu stejnou třídu A z příkladu chování nahrazení, ale tentokrát anotace s předvolbou pro naplnění vlastností místo jejich nahrazení:

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

Pokud spustíte následující deserializační kód, oba Numbers1 a Numbers2 obsahují hodnoty 1, 2, 3, 4, 5 a 6:

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

Příklad vlastnosti Struktury

Následující třída obsahuje vlastnost struktury, S1jejíž deserializační chování je nastaveno na Populate. Po provedení tohoto kódu c.S1.Value1 má hodnotu 10 (z konstruktoru) a c.S1.Value2 má hodnotu 5 (z 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; }
}

Pokud se místo toho použilo výchozí Replace chování, c.S1.Value1 měla by po deserializaci výchozí hodnotu 0. Je to proto, že by se konstruktor C() volal, nastavení c.S1.Value1 na hodnotu 10, ale hodnota S1 by se nahradila novou instancí. (c.S1.Value2 bude stále 5, protože JSON nahradí výchozí hodnotu.)

Jak zadat

Existuje několik způsobů, jak určit předvolbu nahrazení nebo naplnění:

  • Pomocí atributu JsonObjectCreationHandlingAttribute můžete anotovat na úrovni typu nebo vlastnosti. Pokud nastavíte atribut na úrovni typu a nastavíte jeho Handling vlastnost Populatena , chování se použije pouze u těchto vlastností, kde je možné základní soubor (například typy hodnot musí mít setter).

    Pokud chcete, aby Populatebyla předvolba pro celý typ , ale chcete vyloučit jednu nebo více vlastností z daného chování, můžete přidat atribut na úrovni typu a znovu na úrovni vlastnosti přepsat zděděné chování. Tento vzor je zobrazený v následujícím kódu.

    // 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];
    }
    
  • Nastavte JsonSerializerOptions.PreferredObjectCreationHandling (nebo pro generování JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandlingzdroje) a určete globální předvolbu.

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