Jak používat generování zdroje v System.Text.Json
Generování zdrojového kódu System.Text.Json je k dispozici v .NET 6 a novějších verzích. Při použití v aplikaci musí být jazyková verze aplikace C# 9.0 nebo novější. V tomto článku se dozvíte, jak ve svých aplikacích používat serializaci založené na zdrojové generaci.
Informace o různých režimech generování zdroje najdete v tématu Režimy generování zdroje.
Použití výchozích hodnot generování zdroje
Použití generování zdroje se všemi výchozími nastaveními (oba režimy, výchozí možnosti):
Vytvořte částečnou třídu, která je odvozena od JsonSerializerContext.
Zadejte typ pro serializaci nebo deserializaci použitím JsonSerializableAttribute třídy kontextu.
Volejte metodu JsonSerializer , která:
- JsonTypeInfo<T> Přebírá instanci nebo
- JsonSerializerContext Přebírá instanci nebo
- JsonSerializerOptions Vezme instanci a jeho vlastnost jste nastavili JsonSerializerOptions.TypeInfoResolver na
Default
vlastnost typu kontextu (pouze .NET 7 a novější).
Ve výchozím nastavení se používají oba režimy generování zdroje, pokud ho nezadáte. Informace o tom, jak určit režim, který se má použít, naleznete v části Určení režimu generování zdroje dále v tomto článku.
Tady je typ, který se používá v následujících příkladech:
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Tady je kontextová třída nakonfigurovaná tak, aby generovala zdroj pro předchozí WeatherForecast
třídu:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
Typy WeatherForecast
členů není nutné explicitně zadávat pomocí [JsonSerializable]
atributů. Členové deklarovaní jako object
výjimka tohoto pravidla. Typ modulu runtime pro člen deklarovaný podle object
potřeby. Předpokládejme například, že máte následující třídu:
public class WeatherForecast
{
public object? Data { get; set; }
public List<object>? DataList { get; set; }
}
A víte, že za běhu může mít boolean
a int
objekty:
WeatherForecast wf = new() { Data = true, DataList = new List<object> { true, 1 } };
Pak boolean
je int
nutné deklarovat jako [JsonSerializable]
:
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}
Chcete-li zadat generování zdroje pro kolekci, použijte [JsonSerializable]
s typem kolekce. Například: [JsonSerializable(typeof(List<WeatherForecast>))]
.
JsonSerializer
metody, které používají generování zdroje
V následujících příkladech statická Default
vlastnost typu kontextu poskytuje instanci typu kontextu s výchozími možnostmi. Instance kontextu poskytuje WeatherForecast
vlastnost, která vrací JsonTypeInfo<WeatherForecast>
instanci. Pro tuto vlastnost můžete zadat jiný název pomocí TypeInfoPropertyName vlastnosti atributu [JsonSerializable]
.
Příklady serializace
Pomocí JsonTypeInfo<T>:
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
Pomocí JsonSerializerContext:
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
Pomocí JsonSerializerOptions:
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);
Příklady deserializace
Pomocí JsonTypeInfo<T>:
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, SourceGenerationContext.Default.WeatherForecast);
Pomocí JsonSerializerContext:
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
Pomocí JsonSerializerOptions:
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;
Příklad kompletního programu
Tady jsou předchozí příklady v úplném programu:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace BothModesNoOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
public class Program
{
public static void Main()
{
string jsonString = """
{
"Date": "2019-08-01T00:00:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
""";
WeatherForecast? weatherForecast;
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
}
}
}
Určení režimu generování zdroje
Můžete zadat režim založený na metadatech nebo režim optimalizace serializace pro celý kontext, který může obsahovat více typů. Nebo můžete určit režim pro jednotlivé typy. Pokud provedete obojí, specifikace režimu pro typ vyhrává.
- Pro celý kontext použijte JsonSourceGenerationOptionsAttribute.GenerationMode vlastnost.
- Pro jednotlivé typy použijte JsonSerializableAttribute.GenerationMode vlastnost.
Příklad režimu optimalizace serializace (rychlá cesta)
Pro celý kontext:
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { }
Pro jednotlivé typy:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { }
Příklad kompletního programu
using System.Text.Json; using System.Text.Json.Serialization; namespace SerializeOnlyNoOptions { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureCelsius { get; set; } public string? Summary { get; set; } } [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { } [JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { } public class Program { public static void Main() { string jsonString; WeatherForecast weatherForecast = new() { Date = DateTime.Parse("2019-08-01"), TemperatureCelsius = 25, Summary = "Hot" }; // Use context that selects Serialization mode only for WeatherForecast. jsonString = JsonSerializer.Serialize(weatherForecast, SerializeOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} // Use a context that selects Serialization mode. jsonString = JsonSerializer.Serialize(weatherForecast, SerializeOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} } } }
Příklad režimu založeného na metadatech
Pro celý kontext:
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(WeatherForecast))] internal partial class MetadataOnlyContext : JsonSerializerContext { }
jsonString = JsonSerializer.Serialize( weatherForecast!, MetadataOnlyContext.Default.WeatherForecast);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyContext.Default.WeatherForecast);
Pro jednotlivé typy:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Metadata)] internal partial class MetadataOnlyWeatherForecastOnlyContext : JsonSerializerContext { }
jsonString = JsonSerializer.Serialize( weatherForecast!, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);
Příklad kompletního programu
using System.Text.Json; using System.Text.Json.Serialization; namespace MetadataOnlyNoOptions { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureCelsius { get; set; } public string? Summary { get; set; } } [JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Metadata)] internal partial class MetadataOnlyWeatherForecastOnlyContext : JsonSerializerContext { } [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(WeatherForecast))] internal partial class MetadataOnlyContext : JsonSerializerContext { } public class Program { public static void Main() { string jsonString = """ { "Date": "2019-08-01T00:00:00", "TemperatureCelsius": 25, "Summary": "Hot" } """; WeatherForecast? weatherForecast; // Deserialize with context that selects metadata mode only for WeatherForecast only. weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine($"Date={weatherForecast?.Date}"); // output: //Date=8/1/2019 12:00:00 AM // Serialize with context that selects metadata mode only for WeatherForecast only. jsonString = JsonSerializer.Serialize( weatherForecast!, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} // Deserialize with context that selects metadata mode only. weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyContext.Default.WeatherForecast); Console.WriteLine($"Date={weatherForecast?.Date}"); // output: //Date=8/1/2019 12:00:00 AM // Serialize with context that selects metadata mode only. jsonString = JsonSerializer.Serialize( weatherForecast!, MetadataOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} } } }
Podpora generování zdrojového kódu v ASP.NET Core
V aplikacích Blazor použijte přetížení HttpClientJsonExtensions.GetFromJsonAsync a HttpClientJsonExtensions.PostAsJsonAsync rozšiřující metody, které využívají kontext generování zdroje nebo TypeInfo<TValue>
.
Počínaje rozhraním .NET 8 můžete také použít přetížení rozšiřujících HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable metod, které přijímají kontext generování zdroje nebo TypeInfo<TValue>
.
V aplikacích Razor Pages, MVC, SignalR a Web API použijte JsonSerializerOptions.TypeInfoResolver vlastnost k určení kontextu.
[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }
var serializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = MyJsonContext.Default
};
services.AddControllers().AddJsonOptions(
static options =>
options.JsonSerializerOptions.TypeInfoResolverChain.Add(MyJsonContext.Default));
Poznámka:
JsonSourceGenerationMode.Serializationserializace s rychlou cestou není podporována pro asynchronní serializaci.
V .NET 7 a starších verzích se toto omezení vztahuje také na synchronní přetížení JsonSerializer.Serialize , které přijímají Stream. Počínaje rozhraním .NET 8, i když serializace streamování vyžaduje modely založené na metadatech, vrátí se k rychlé cestě, pokud jsou datové části známé, že jsou dostatečně malé, aby se vešly do předem určené velikosti vyrovnávací paměti. Další informace najdete na webu https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json.
Zakázání výchozích hodnot reflexe
Vzhledem k tomu, že System.Text.Json se ve výchozím nastavení používá reflexe, může volání základní metody serializace narušit nativní aplikace AOT, které nepodporují všechna požadovaná rozhraní API reflexe. Tyto přestávky můžou být náročné diagnostikovat, protože můžou být nepředvídatelné a aplikace se často ladí pomocí modulu runtime CoreCLR, kde funguje reflexe. Pokud místo toho explicitně zakážete serializaci založenou na reflexi, je snazší diagnostikovat konce. Kód, který používá serializaci založenou na reflexi, způsobí InvalidOperationException vyvolání popisné zprávy za běhu.
Pokud chcete ve své aplikaci zakázat výchozí reflexi, nastavte JsonSerializerIsReflectionEnabledByDefault
vlastnost MSBuild do false
souboru projektu:
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
- Chování této vlastnosti je konzistentní bez ohledu na modul runtime, buď CoreCLR, nebo nativní AOT.
- Pokud tuto vlastnost nezadáte a funkce PublishTrimmed je povolená , serializace založená na reflexi se automaticky zakáže.
Pomocí této vlastnosti můžete programově zkontrolovat, jestli je reflexe zakázaná JsonSerializer.IsReflectionEnabledByDefault . Následující fragment kódu ukazuje, jak můžete serializátor nakonfigurovat v závislosti na tom, jestli je reflexe povolená:
static JsonSerializerOptions CreateDefaultOptions()
{
return new()
{
TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
? new DefaultJsonTypeInfoResolver()
: MyContext.Default
};
}
Vzhledem k tomu, že vlastnost je považována za konstantu link-time, předchozí metoda neuloží překladač založený na reflexi v aplikacích, které běží v nativní AOT.
Zadání možností
V .NET 8 a novějších verzích lze většinu možností, které můžete použít JsonSerializerOptions , nastavit také pomocí atributu JsonSourceGenerationOptionsAttribute . Výhodou nastavení možností prostřednictvím atributu je to, že konfigurace je určena v době kompilace, což zajišťuje, že vygenerovaná MyContext.Default
vlastnost je předem nakonfigurovaná se všemi příslušnými sadami možností.
Následující kód ukazuje, jak nastavit možnosti pomocí atributu JsonSourceGenerationOptionsAttribute .
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
Při použití JsonSourceGenerationOptionsAttribute
k určení možností serializace, zavolejte jednu z následujících metod serializace:
Metoda
JsonSerializer.Serialize
, která přebíráTypeInfo<TValue>
. Předejte vlastnostDefault.<TypeName>
vaší třídy kontextu:jsonString = JsonSerializer.Serialize( weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
Metoda
JsonSerializer.Serialize
, která přebírá kontext. Předejte jiDefault
statickou vlastnost vaší třídy kontextu.jsonString = JsonSerializer.Serialize( weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
Pokud voláte metodu, která vám umožní předat vlastní instanci Utf8JsonWriter
, nastavení zapisovače Indented se respektuje místo JsonSourceGenerationOptionsAttribute.WriteIndented
možnosti.
Pokud vytvoříte a použijete kontextovou instanci voláním konstruktoru JsonSerializerOptions
, který přebírá instanci, bude zadaná instance použita místo možností určených JsonSourceGenerationOptionsAttribute
.
Tady jsou předchozí příklady v úplném programu:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SerializeOnlyWithOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
public class Program
{
public static void Main()
{
string jsonString;
WeatherForecast weatherForecast = new()
{ Date = DateTime.Parse("2019-08-01"), TemperatureCelsius = 25, Summary = "Hot" };
// Serialize using TypeInfo<TValue> provided by the context
// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}
// Serialize using Default context
// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}
}
}
}
Kombinování generátorů zdrojů
Kontrakty z několika kontextů vygenerovaných zdrojem můžete kombinovat v jedné JsonSerializerOptions instanci. JsonSerializerOptions.TypeInfoResolver Pomocí vlastnosti zřetězte více kontextů, které byly kombinovány pomocí JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]) metody.
var options = new JsonSerializerOptions
{
TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default);
};
Počínaje rozhraním .NET 8, pokud později chcete předem připojit nebo připojit jiný kontext, můžete to provést pomocí JsonSerializerOptions.TypeInfoResolverChain vlastnosti. Řazení řetězce je významné: JsonSerializerOptions dotazuje se na každý z překladačů v zadaném pořadí a vrátí první výsledek, který není null.
options.TypeInfoResolverChain.Add(ContextD.Default); // Append to the end of the list.
options.TypeInfoResolverChain.Insert(0, ContextE.Default); // Insert at the beginning of the list.
Všechny změny provedené ve TypeInfoResolverChain vlastnosti se projeví TypeInfoResolver naopak.
Serializace výčtových polí jako řetězců
Ve výchozím nastavení jsou výčty serializovány jako čísla. Pokud chcete serializovat pole určitého výčtu jako řetězce při použití generování zdroje, anotujte je pomocí převaděče JsonStringEnumConverter<TEnum> . Nebo nastavit deka zásady pro všechny výčty, použijte JsonSourceGenerationOptionsAttribute atribut.
JsonStringEnumConverter<T>
převodník
Chcete-li serializovat názvy výčtů jako řetězce pomocí generování zdroje, použijte JsonStringEnumConverter<TEnum> převaděč. (Nativní modul runtime AOT nepodporuje obecný JsonStringEnumConverter typ.)
Anotujte typ výčtu pomocí převaděče JsonStringEnumConverter<TEnum> pomocí atributu JsonConverterAttribute :
public class WeatherForecastWithPrecipEnum
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation? Precipitation { get; set; }
}
[JsonConverter(typeof(JsonStringEnumConverter<Precipitation>))]
public enum Precipitation
{
Drizzle, Rain, Sleet, Hail, Snow
}
Vytvořte JsonSerializerContext třídu a označte ji pomocí atributu JsonSerializableAttribute :
[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }
Následující kód serializuje názvy výčtů místo číselných hodnot:
var weatherForecast = new WeatherForecastWithPrecipEnum
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Precipitation = Precipitation.Sleet
};
var options = new JsonSerializerOptions
{
WriteIndented = true,
TypeInfoResolver = Context1.Default,
};
string? jsonString = JsonSerializer.Serialize(weatherForecast, options);
Výsledný json vypadá jako v následujícím příkladu:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}
Zásady deky
Místo použití JsonStringEnumConverter<TEnum> typu můžete použít deka zásady serializace výčtů jako řetězce pomocí JsonSourceGenerationOptionsAttribute. Vytvořte JsonSerializerContext třídu a označte ji poznámkami JsonSerializableAttribute a JsonSourceGenerationOptionsAttribute atributy:
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }
Všimněte si, že výčt nemá následující JsonConverterAttribute:
public class WeatherForecast2WithPrecipEnum
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation2? Precipitation { get; set; }
}
public enum Precipitation2
{
Drizzle, Rain, Sleet, Hail, Snow
}
Vlastní názvy členů výčtu
Od verze .NET 9 můžete přizpůsobit názvy členů výčtu pomocí atributu JsonStringEnumMemberName. Další informace naleznete v tématu Vlastní názvy členů výčtu.