Så här instansierar du JsonSerializerOptions-instanser med System.Text.Json

Den här artikeln beskriver hur du undviker prestandaproblem när du använder JsonSerializerOptions. Den visar också hur du använder de parameteriserade konstruktorerna som är tillgängliga.

ÅteranvändA JsonSerializerOptions-instanser

Om du använder JsonSerializerOptions upprepade gånger med samma alternativ ska du inte skapa en ny JsonSerializerOptions instans varje gång du använder den. Återanvänd samma instans för varje anrop. Den här vägledningen gäller för kod som du skriver för anpassade konverterare och när du anropar JsonSerializer.Serialize eller JsonSerializer.Deserialize. Det är säkert att använda samma instans i flera trådar. Metadatacacheminnena på alternativinstansen är trådsäkra och instansen är oföränderlig efter den första serialiseringen eller deserialiseringen.

Följande kod visar prestandastraffet för användning av nya alternativinstanser.

using System.Diagnostics;
using System.Text.Json;

namespace OptionsPerfDemo
{
    public record Forecast(DateTime Date, int TemperatureC, string Summary);

    public class Program
    {
        public static void Main()
        {
            Forecast forecast = new(DateTime.Now, 40, "Hot");
            JsonSerializerOptions options = new() { WriteIndented = true };
            int iterations = 100000;

            var watch = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                Serialize(forecast, options);
            }
            watch.Stop();
            Console.WriteLine($"Elapsed time using one options instance: {watch.ElapsedMilliseconds}");

            watch = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                Serialize(forecast);
            }
            watch.Stop();
            Console.WriteLine($"Elapsed time creating new options instances: {watch.ElapsedMilliseconds}");
        }

        private static void Serialize(Forecast forecast, JsonSerializerOptions? options = null)
        {
            _ = JsonSerializer.Serialize<Forecast>(
                forecast,
                options ?? new JsonSerializerOptions() { WriteIndented = true });
        }
    }
}

// Produces output like the following example:
//
//Elapsed time using one options instance: 190
//Elapsed time creating new options instances: 40140

Föregående kod serialiserar ett litet objekt 100 000 gånger med samma alternativinstans. Sedan serialiserar det samma objekt samma antal gånger och skapar en ny alternativinstans varje gång. En vanlig tidsskillnad är 190 jämfört med 40 140 millisekunder. Skillnaden är ännu större om du ökar antalet iterationer.

Serialiseraren genomgår en uppvärmningsfas under den första serialiseringen av varje typ i objektdiagrammet när en ny alternativinstans skickas till den. Den här uppvärmningen omfattar att skapa en cache med metadata som behövs för serialisering. Metadata innehåller ombud för egenskapsmottagare, setters, konstruktorargument, angivna attribut och så vidare. Den här metadatacachen lagras i alternativinstansen. Samma uppvärmningsprocess och cache gäller för deserialisering.

Storleken på metadatacachen i en JsonSerializerOptions instans beror på antalet typer som ska serialiseras. Om du skickar flera typer, till exempel dynamiskt genererade typer, till serialiseraren fortsätter cachestorleken att växa och kan orsaka en OutOfMemoryException.

Egenskapen JsonSerializerOptions.Default

Om den instans som JsonSerializerOptions du behöver använda är standardinstansen (har alla standardinställningar och standardkonverterare) använder du JsonSerializerOptions.Default egenskapen i stället för att skapa en alternativinstans. Mer information finns i Använda standardsystemkonverterare.

Kopiera JsonSerializerOptions

Det finns en JsonSerializerOptions-konstruktor som gör att du kan skapa en ny instans med samma alternativ som en befintlig instans, som du ser i följande exempel:

using System.Text.Json;

namespace CopyOptions
{
    public class Forecast
    {
        public DateTime Date { get; init; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
    };

    public class Program
    {
        public static void Main()
        {
            Forecast forecast = new()
            {
                Date = DateTime.Now,
                TemperatureC = 40,
                Summary = "Hot"
            };

            JsonSerializerOptions options = new()
            {
                WriteIndented = true
            };

            JsonSerializerOptions optionsCopy = new(options);
            string forecastJson =
                JsonSerializer.Serialize<Forecast>(forecast, optionsCopy);

            Console.WriteLine($"Output JSON:\n{forecastJson}");
        }
    }
}

// Produces output like the following example:
//
//Output JSON:
//{
//  "Date": "2020-10-21T15:40:06.8998502-07:00",
//  "TemperatureC": 40,
//  "Summary": "Hot"
//}
Imports System.Text.Json
Imports System.Text.Json.Serialization

Namespace CopyOptions

    Public Class Forecast
        Public Property [Date] As Date
        Public Property TemperatureC As Integer
        Public Property Summary As String
    End Class

    Public NotInheritable Class Program

        Public Shared Sub Main()
            Dim forecast1 As New Forecast() With {
                .[Date] = Date.Now,
                .Summary = Nothing,
                .TemperatureC = CType(Nothing, Integer)
            }

            Dim options As New JsonSerializerOptions() With {
                .DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
            }

            Dim optionsCopy As New JsonSerializerOptions
            Dim forecastJson As String = JsonSerializer.Serialize(forecast1, optionsCopy)

            Console.WriteLine($"Output JSON:{forecastJson}")
        End Sub

    End Class

End Namespace

' Produces output like the following example:
'
'Output JSON:
'{
'  "Date": "2020-10-21T15:40:06.8998502-07:00",
'  "TemperatureC": 40,
'  "Summary": "Hot"
'}

Metadatacachen för den befintliga JsonSerializerOptions instansen kopieras inte till den nya instansen. Så att använda den här konstruktorn är inte detsamma som att återanvända en befintlig instans av JsonSerializerOptions.

Webbstandarder för JsonSerializerOptions

Följande alternativ har olika standardinställningar för webbappar:

I .NET 9 och senare versioner kan du använda singleton för JsonSerializerOptions.Web att serialisera med standardalternativen som ASP.NET Core använder för webbappar. I tidigare versioner anropar du konstruktorn JsonSerializerOptions för att skapa en ny instans med webbstandardvärdena, som du ser i följande exempel:

using System.Text.Json;

namespace OptionsDefaults
{
    public class Forecast
    {
        public DateTime? Date { get; init; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
    };

    public class Program
    {
        public static void Main()
        {
            Forecast forecast = new()
            {
                Date = DateTime.Now,
                TemperatureC = 40,
                Summary = "Hot"
            };

            JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
            {
                WriteIndented = true
            };

            Console.WriteLine(
                $"PropertyNameCaseInsensitive: {options.PropertyNameCaseInsensitive}");
            Console.WriteLine(
                $"JsonNamingPolicy: {options.PropertyNamingPolicy}");
            Console.WriteLine(
                $"NumberHandling: {options.NumberHandling}");

            string forecastJson = JsonSerializer.Serialize<Forecast>(forecast, options);
            Console.WriteLine($"Output JSON:\n{forecastJson}");

            Forecast? forecastDeserialized =
                JsonSerializer.Deserialize<Forecast>(forecastJson, options);

            Console.WriteLine($"Date: {forecastDeserialized?.Date}");
            Console.WriteLine($"TemperatureC: {forecastDeserialized?.TemperatureC}");
            Console.WriteLine($"Summary: {forecastDeserialized?.Summary}");
        }
    }
}

// Produces output like the following example:
//
//PropertyNameCaseInsensitive: True
//JsonNamingPolicy: System.Text.Json.JsonCamelCaseNamingPolicy
//NumberHandling: AllowReadingFromString
//Output JSON:
//{
//  "date": "2020-10-21T15:40:06.9040831-07:00",
//  "temperatureC": 40,
//  "summary": "Hot"
//}
//Date: 10 / 21 / 2020 3:40:06 PM
//TemperatureC: 40
//Summary: Hot
Imports System.Text.Json

Namespace OptionsDefaults

    Public Class Forecast
        Public Property [Date] As Date
        Public Property TemperatureC As Integer
        Public Property Summary As String
    End Class

    Public NotInheritable Class Program

        Public Shared Sub Main()
            Dim forecast1 As New Forecast() With {
                .[Date] = Date.Now,
                .TemperatureC = 40,
                .Summary = "Hot"
                }

            Dim options As New JsonSerializerOptions(JsonSerializerDefaults.Web) With {
                .WriteIndented = True
                }

            Console.WriteLine(
                $"PropertyNameCaseInsensitive: {options.PropertyNameCaseInsensitive}")
            Console.WriteLine(
                $"JsonNamingPolicy: {options.PropertyNamingPolicy}")
            Console.WriteLine(
                $"NumberHandling: {options.NumberHandling}")

            Dim forecastJson As String = JsonSerializer.Serialize(forecast1, options)
            Console.WriteLine($"Output JSON:{forecastJson}")

            Dim forecastDeserialized As Forecast = JsonSerializer.Deserialize(Of Forecast)(forecastJson, options)

            Console.WriteLine($"Date: {forecastDeserialized.[Date]}")
            Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}")
            Console.WriteLine($"Summary: {forecastDeserialized.Summary}")
        End Sub

    End Class

End Namespace

' Produces output like the following example:
'
'PropertyNameCaseInsensitive: True
'JsonNamingPolicy: System.Text.Json.JsonCamelCaseNamingPolicy
'NumberHandling: AllowReadingFromString
'Output JSON:
'{
'  "date": "2020-10-21T15:40:06.9040831-07:00",
'  "temperatureC": 40,
'  "summary": "Hot"
'}
'Date: 10 / 21 / 2020 3:40:06 PM
'TemperatureC: 40
'Summary: Hot