Share via


Cara membuat instans JsonSerializerOptions dengan System.Text.Json

Artikel ini menjelaskan cara menghindari masalah performa saat Anda menggunakan JsonSerializerOptions. Ini juga menunjukkan cara menggunakan konstruktor berparameter yang tersedia.

Menggunakan kembali instans JsonSerializerOptions

Jika Anda menggunakan JsonSerializerOptions berulang kali dengan opsi yang sama, jangan buat instans JsonSerializerOptions baru setiap kali menggunakannya. Gunakan kembali instans yang sama untuk setiap panggilan. Panduan ini berlaku untuk kode yang Anda tulis untuk pengonversi kustom dan saat Anda memanggil JsonSerializer.Serialize atau JsonSerializer.Deserialize. Aman untuk menggunakan instans yang sama di beberapa utas. Cache metadata pada instans opsi aman untuk utas, dan instans tidak dapat diubah setelah serialisasi atau deserialisasi pertama.

Kode berikut menunjukkan penalti performa untuk menggunakan instans opsi baru.

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

Kode sebelumnya membuat serial objek kecil 100.000 kali menggunakan instans opsi yang sama. Kemudian kode akan membuat serial objek yang sama dengan jumlah yang sama dan membuat instans opsi baru setiap kali. Perbedaan durasi yang khas adalah 190 dibandingkan dengan 40.140 milidetik. Perbedaannya bahkan lebih besar jika Anda meningkatkan jumlah perulangan.

Serializer menjalani fase pemanasan selama serialisasi pertama dari setiap jenis dalam grafik objek ketika instans opsi baru diteruskan ke sana. Pemanasan ini mencakup pembuatan cache metadata yang diperlukan untuk serialisasi. Metadata mencakup delegasi ke pengambil properti, pengatur, argumen konstruktor, atribut yang ditentukan, dan sebagainya. Cache metadata ini disimpan dalam instans opsi. Proses pemanasan dan cache yang sama berlaku untuk deserialisasi.

Ukuran cache metadata dalam instans JsonSerializerOptions tergantung jumlah jenis yang akan diserialisasikan. Jika Anda meneruskan banyak jenis—misalnya, jenis yang dihasilkan secara dinamis—ke serializer, ukuran cache akan terus bertambah dan akhirnya dapat menyebabkan OutOfMemoryException.

Properti JsonSerializerOptions.Default

Jika instans JsonSerializerOptions yang perlu Anda gunakan adalah instans default (memiliki semua pengaturan default dan pengonversi default), gunakan properti JsonSerializerOptions.Default daripada membuat instans opsi. Untuk informasi selengkapnya, lihat Menggunakan pengonversi sistem default.

Salin JsonSerializerOptions

Ada konstruktor JsonSerializerOptions yang memungkinkan Anda membuat instans baru dengan opsi yang sama dengan instans yang ada, seperti yang ditunjukkan dalam contoh berikut:

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

Cache metadata dari instans JsonSerializerOptions yang ada tidak disalin ke instans baru. Jadi menggunakan konstruktor ini tidak sama dengan menggunakan kembali instans yang ada dari JsonSerializerOptions.

Default web untuk JsonSerializerOptions

Opsi berikut memiliki default yang berbeda untuk aplikasi web:

Di .NET 9 dan versi yang lebih baru, Anda dapat menggunakan JsonSerializerOptions.Web singleton untuk diserialisasikan dengan opsi default yang ASP.NET Core gunakan untuk aplikasi web. Di versi sebelumnya, panggil konstruktor JsonSerializerOptions untuk membuat instans baru dengan default web, seperti yang ditunjukkan dalam contoh berikut:

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