Vytvoření instance instancí JsonSerializerOptions pomocí System.Text.Json

Tento článek vysvětluje, jak se vyhnout problémům s výkonem při použití JsonSerializerOptions. Ukazuje také, jak používat parametrizované konstruktory, které jsou k dispozici.

Opakované použití instancí JsonSerializerOptions

Pokud používáte JsonSerializerOptions opakovaně se stejnými možnostmi, nevytvávejte při každém použití novou JsonSerializerOptions instanci. Znovu použijte stejnou instanci pro každé volání. Tyto pokyny platí pro kód, který píšete pro vlastní převaděče a při volání JsonSerializer.Serialize nebo JsonSerializer.Deserialize. Je bezpečné používat stejnou instanci ve více vláknech. Metadata v instanci možností jsou bezpečná pro přístup z více vláken a instance je neměnná po první serializaci nebo deserializaci.

Následující kód ukazuje snížení výkonu za použití nových instancí možností.

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

Předchozí kód serializuje malý objekt 100 000 krát pomocí stejné instance možností. Potom serializuje stejný objekt stejný početkrát a pokaždé vytvoří novou instanci možností. Typický rozdíl v době běhu je 190 ve srovnání se 40 140 milisekundami. Rozdíl je ještě větší, pokud zvýšíte počet iterací.

Serializátor prochází fázi zahřátí během první serializace každého typu v grafu objektu, když je do ní předána nová instance možností. Toto zahřátí zahrnuje vytvoření mezipaměti metadat potřebných pro serializaci. Metadata zahrnují delegáty na getters, setters, konstruktor argumenty, zadané atributy atd. Tato mezipaměť metadat je uložena v instanci možností. Stejný proces zahřátí a mezipaměť platí pro deserializaci.

Velikost mezipaměti metadat v JsonSerializerOptions instanci závisí na počtu typů, které se mají serializovat. Pokud předáte serializátoru mnoho typů , například dynamicky generovaných typů, velikost mezipaměti se bude dál zvětšovat a může skončit příčinou OutOfMemoryException.

Vlastnost JsonSerializerOptions.Default

Pokud je instance JsonSerializerOptions , kterou potřebujete použít, výchozí instance (má všechna výchozí nastavení a výchozí převaděče), použijte JsonSerializerOptions.Default tuto vlastnost místo vytváření instance možností. Další informace naleznete v tématu Použití výchozího systémového převaděče.

Kopírování JsonSerializerOptions

Existuje konstruktor JsonSerializerOptions, který umožňuje vytvořit novou instanci se stejnými možnostmi jako existující instance, jak je znázorněno v následujícím příkladu:

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"
'}

Mezipaměť metadat existující JsonSerializerOptions instance se do nové instance nezkopíruje. Použití tohoto konstruktoru tedy není stejné jako opakované použití existující instance JsonSerializerOptions.

Výchozí nastavení webu pro JsonSerializerOptions

Následující možnosti mají různé výchozí hodnoty pro webové aplikace:

V .NET 9 a novějších verzích můžete použít JsonSerializerOptions.Web singleton k serializaci s výchozími možnostmi, které ASP.NET Core používá pro webové aplikace. V dřívějších verzích volejte konstruktor JsonSerializerOptions, který vytvoří novou instanci s výchozími nastaveními webu, jak je znázorněno v následujícím příkladu:

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