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:
- PropertyNameCaseInsensitive =
true
- PropertyNamingPolicy = CamelCase
- NumberHandling = AllowReadingFromString
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