Share via


Auffüllen initialisierter Eigenschaften

Ab .NET 8 können Sie eine Einstellung zum Ersetzen oder Auffüllen von .NET-Eigenschaften angeben, wenn JSON deserialisiert wird. Die JsonObjectCreationHandling-Enumeration bietet die Auswahlmöglichkeiten für die Objekterstellung:

Standardverhalten (Ersetzen)

Der System.Text.Json-Deserialisierer erstellt immer eine neue Instanz des Zieltyps. Obwohl eine neue Instanz erstellt wird, werden einige Eigenschaften und Felder möglicherweise bereits als Teil der Konstruktion des Objekts initialisiert. Berücksichtigen Sie den folgenden -Typ:

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

Wenn Sie eine Instanz dieser Klasse erstellen, ist der Numbers1- (und Numbers2-)Wert der Eigenschaft eine Liste mit drei Elementen (1, 2 und 3). Wenn Sie JSON für diesen Typ deserialisieren, besteht das Standardverhalten darin, dass Eigenschaftswerte ersetzt werden:

  • Da Numbers1 schreibgeschützt ist (kein Setter), weist es weiterhin die Werte 1, 2 und 3 in der Liste auf.
  • Da Numbers2 Lese-/Schreibzugriff hat, wird eine neue Liste zugewiesen, und die Werte aus dem JSON-Code werden hinzugefügt.

Wenn Sie beispielsweise den folgenden Deserialisierungscode ausführen, enthält Numbers1 die Werte 1, 2 und 3 und Numbers2 die Werte 4, 5 und 6.

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

Auffüllverhalten

Ab .NET 8 können Sie das Deserialisierungsverhalten ändern, um Eigenschaften und Felder zu ändern (auffüllen), anstatt sie zu ersetzen:

  • Bei einer Auflistungstypeigenschaft wird das Objekt ohne Löschen wiederverwendet. Wenn die Auflistung mit Elementen vorgefüllt ist, werden sie zusammen mit den Werten aus dem JSON-Code im endgültigen deserialisierten Ergebnis angezeigt. Ein Beispiel finden Sie unter Beispiel für Sammlungseigenschaften.

  • Bei einer Eigenschaft, die ein Objekt mit Eigenschaften ist, werden die veränderbaren Eigenschaften auf die JSON-Werte aktualisiert, aber der Objektverweis selbst ändert sich nicht.

  • Bei einer Strukturtypeigenschaft besteht das effektive Verhalten darin, dass für seine veränderbaren Eigenschaften alle vorhandene Werte beibehalten und neue Werte aus dem JSON-Code hinzugefügt werden. Im Gegensatz zu einer Referenzeigenschaft wird das Objekt selbst jedoch nicht wiederverwendet, da es sich um einen Werttyp handelt. Stattdessen wird eine Kopie der Struktur geändert und dann der Eigenschaft neu zugewiesen. Ein Beispiel finden Sie unter Beispiel für Struktureigenschaften.

    Eine Struktureigenschaft muss über einen Setter verfügen; andernfalls wird zur Laufzeit ein InvalidOperationException ausgelöst.

Hinweis

Das Auffüllverhalten funktioniert derzeit nicht für Typen mit parametrisiertem Konstruktor. Weitere Informationen finden Sie unter dotnet/runtime issue 92877.

Schreibgeschützte Eigenschaften

Da die Instanz, auf die die Eigenschaft verweist, nicht ersetztwird, ist zum Auffüllen von Referenzeigenschaften keinen Setter erforderlich. Dieses Verhalten bedeutet, dass die Deserialisierung auch schreibgeschützte Eigenschaften auffüllen kann.

Hinweis

Struktureigenschaften erfordern weiterhin Setter, da die Instanz durch eine geänderte Kopie ersetzt wird.

Beispiel für Sammlungseigenschaften

Betrachten Sie dieselbe A-Klasse aus dem Beispiel für das Ersetzungsverhalten, aber dieses Mal mit einer Einstellung zum Auffüllen von Eigenschaften und nicht zum Ersetzen:

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

Wenn Sie den folgenden Deserialisierungscode ausführen, enthalten Numbers1 und Numbers2 die Werte 1, 2, 3, 4, 5 und 6:

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

Beispiel für Struktureigenschaften

Die folgende Klasse enthält die Struktureigenschaft „S1“, deren Deserialisierungsverhalten auf Populate festgelegt ist. Nach dem Ausführen dieses Codes hat c.S1.Value1 den Wert 10 (aus dem Konstruktor) und c.S1.Value2 den Wert 5 (aus dem 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; }
}

Wenn stattdessen das Standardverhalten „Replace“ verwendet wurde, würde c.S1.Value1 nach der Deserialisierung den Standardwert 0 aufweisen. Das liegt daran, dass der Konstruktor „C()“ aufgerufen und c.S1.Value1 auf 10 festgelegt würde, anschließend würde aber der Wert S1 durch eine neue Instanz ersetzt. (c.S1.Value2 wäre immer noch 5, da der JSON den Standardwert ersetzt.)

So geben Sie Einstellungen an

Es gibt mehrere Möglichkeiten, eine Einstellung zum Ersetzen oder Auffüllen anzugeben:

  • Verwenden Sie das JsonObjectCreationHandlingAttribute-Attribut zum Kommentieren auf Typ- oder Eigenschaftsebene. Wenn Sie das Attribut auf der Typebene festlegen und dessen Handling-Eigenschaft auf „Populate“ festlegen, gilt das Verhalten nur für die Eigenschaften, bei denen die Grundpopulation möglich ist (z. B. müssen Werttypen einen Setter haben).

    Wenn Sie möchten, dass die typweite Einstellung „Populate“ ist, aber eine oder mehrere Eigenschaften aus diesem Verhalten ausgeschlossen werden sollen, können Sie das Attribut auf Typebene und erneut auf Eigenschaftsebene hinzufügen, um das geerbte Verhalten außer Kraft zu setzen. Dieses Muster wird im folgenden Code gezeigt.

    // 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];
    }
    
  • Legen Sie JsonSerializerOptions.PreferredObjectCreationHandling (oder für die Quellgenerierung JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling) fest, um eine globale Einstellung anzugeben.

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