Fyll i initierade egenskaper
Från och med .NET 8 kan du ange en inställning för att antingen ersätta eller fylla i .NET-egenskaper när JSON deserialiseras. Uppräkningen JsonObjectCreationHandling innehåller alternativen för hantering av objektskapande:
Standardbeteende (ersätt)
Deserialiseraren System.Text.Json skapar alltid en ny instans av måltypen. Men även om en ny instans skapas kan vissa egenskaper och fält redan initieras som en del av objektets konstruktion. Tänk på följande typ:
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
När du skapar en instans av den här klassen Numbers1
är egenskapen (och Numbers2
) värdet en lista med tre element (1, 2 och 3). Om du deserialiserar JSON till den här typen är standardbeteendet att egenskapsvärden ersätts:
- För
Numbers1
, eftersom den är skrivskyddad (ingen setter) har den fortfarande värdena 1, 2 och 3 i listan. - För
Numbers2
, som är skrivskyddad, allokeras en ny lista och värdena från JSON läggs till.
Om du till exempel kör följande deserialiseringskod innehåller Numbers1
värdena 1, 2 och 3 och Numbers2
innehåller värdena 4, 5 och 6.
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
Fylla i beteende
Från och med .NET 8 kan du ändra deserialiseringsbeteendet för att ändra (fylla i) egenskaper och fält i stället för att ersätta dem:
För en samlingstypegenskap återanvänds objektet utan att rensas. Om samlingen är förifyllda med element visas de i det slutliga deserialiserade resultatet tillsammans med värdena från JSON. Ett exempel finns i Exempel på samlingsegenskap.
För en egenskap som är ett objekt med egenskaper uppdateras dess föränderliga egenskaper till JSON-värdena, men själva objektreferensen ändras inte.
För en structtypegenskap är det effektiva beteendet att för dess föränderliga egenskaper behålls alla befintliga värden och nya värden från JSON läggs till. Men till skillnad från en referensegenskap återanvänds inte själva objektet eftersom det är en värdetyp. I stället ändras en kopia av structen och tilldelas sedan om till egenskapen. Ett exempel finns i Exempel på Struct-egenskap.
En struct-egenskap måste ha en setter. annars utlöses en InvalidOperationException vid körning.
Kommentar
Det ifyllda beteendet fungerar för närvarande inte för typer som har en parametriserad konstruktor. Mer information finns i dotnet/runtime issue 92877.
Skrivskyddade egenskaper
För att fylla i referensegenskaper som är föränderliga, eftersom den instans som egenskapen refererar till inte ersätts, behöver egenskapen inte ha en setter. Det här beteendet innebär att deserialisering också kan fylla i skrivskyddade egenskaper.
Kommentar
Struct-egenskaper kräver fortfarande setters eftersom instansen ersätts med en ändrad kopia.
Exempel på samlingsegenskap
Överväg samma klass A
från exemplet med ersättningsbeteende, men den här gången kommenterad med en inställning för att fylla i egenskaper i stället för att ersätta dem:
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
Om du kör följande deserialiseringskod, både Numbers1
och Numbers2
innehåller värdena 1, 2, 3, 4, 5 och 6:
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
Exempel på struct-egenskap
Följande klass innehåller en struct-egenskap, , S1
vars deserialiseringsbeteende är inställt på Populate. När du har kört den här koden c.S1.Value1
har du värdet 10 (från konstruktorn) och c.S1.Value2
har värdet 5 (från 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; }
}
Om standardbeteendet Replace användes i stället skulle c.S1.Value1
standardvärdet vara 0 efter deserialisering. Det beror på att konstruktorn C()
anropas och anger c.S1.Value1
till 10, men sedan ersätts värdet för S1 med en ny instans. (c.S1.Value2
skulle fortfarande vara 5, eftersom JSON ersätter standardvärdet.)
Så här anger du
Det finns flera sätt att ange en inställning för att ersätta eller fylla i:
JsonObjectCreationHandlingAttribute Använd attributet för att kommentera på typ- eller egenskapsnivå. Om du anger attributet på typnivå och anger dess Handling egenskap till Populategäller beteendet endast för de egenskaper där populationen är möjlig (till exempel måste värdetyper ha en setter).
Om du vill att den typomfattande inställningen ska vara Populate, men vill undanta en eller flera egenskaper från det beteendet, kan du lägga till attributet på typnivå och igen på egenskapsnivå för att åsidosätta det ärvda beteendet. Det mönstret visas i följande kod.
// 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]; }
Ange JsonSerializerOptions.PreferredObjectCreationHandling (eller, för källgenerering, JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling) för att ange en global inställning.
var options = new JsonSerializerOptions { PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate };