Как использовать создание источника в System.Text.Json
Создание System.Text.Json источника доступно в .NET 6 и более поздних версиях. При использовании в приложении языковая версия приложения должна быть C# 9.0 или более поздней. В этой статье показано, как использовать сериализацию с поддержкой исходного поколения в приложениях.
Сведения о различных режимах создания источника см. в разделе "Режимы создания источников".
Использование значений по умолчанию для создания источников
Чтобы использовать создание источника со всеми значениями по умолчанию (оба режима, параметры по умолчанию):
Создайте частичный класс, производный от JsonSerializerContext.
Укажите тип для сериализации или десериализации, применяя JsonSerializableAttribute к классу контекста.
JsonSerializer Вызовите метод, который:
- JsonTypeInfo<T> Принимает экземпляр или
- JsonSerializerContext Принимает экземпляр или
- JsonSerializerOptions Принимает экземпляр и задается его JsonSerializerOptions.TypeInfoResolver свойство
Default
свойством типа контекста (только .NET 7 и более поздних версий).
По умолчанию оба режима создания источника используются, если не указать один. Сведения о том, как указать режим использования, см. далее в этой статье.
Ниже приведен тип, используемый в следующих примерах:
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Ниже приведен класс контекста, настроенный для создания источника для предыдущего WeatherForecast
класса:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
Типы WeatherForecast
элементов не нужно явно указывать с [JsonSerializable]
атрибутами. Члены, объявленные как object
исключение из этого правила. Тип среды выполнения для элемента, объявленного как object
необходимо указать. Например, предположим, что у вас есть следующий класс:
public class WeatherForecast
{
public object? Data { get; set; }
public List<object>? DataList { get; set; }
}
И вы знаете, что во время выполнения он может иметь boolean
и int
объекты:
WeatherForecast wf = new() { Data = true, DataList = new List<object> { true, 1 } };
Затем boolean
и int
должны быть объявлены следующим образом [JsonSerializable]
:
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}
Чтобы указать создание источника для коллекции, используйте [JsonSerializable]
его с типом коллекции. Например: [JsonSerializable(typeof(List<WeatherForecast>))]
.
JsonSerializer
методы, использующие создание источника
В следующих примерах статическое Default
свойство типа контекста предоставляет экземпляр типа контекста с параметрами по умолчанию. Экземпляр контекста предоставляет WeatherForecast
свойство, возвращающее JsonTypeInfo<WeatherForecast>
экземпляр. Для этого свойства можно указать другое имя, используя TypeInfoPropertyName свойство атрибута [JsonSerializable]
.
Примеры сериализации
Использование среды JsonTypeInfo<T>:
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
Использование среды JsonSerializerContext:
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
Использование среды JsonSerializerOptions:
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);
Примеры десериализации
Использование среды JsonTypeInfo<T>:
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, SourceGenerationContext.Default.WeatherForecast);
Использование среды JsonSerializerContext:
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
Использование среды JsonSerializerOptions:
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;
Полный пример программы
Ниже приведены приведенные выше примеры в полной программе:
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"}
}
}
}
Указание режима создания источника
Можно указать режим на основе метаданных или режим оптимизации сериализации для всего контекста, который может включать несколько типов. Или можно указать режим для отдельного типа. В обоих случаях спецификация режима для типа выигрывает.
- Для всего контекста JsonSourceGenerationOptionsAttribute.GenerationMode используйте свойство.
- Для отдельного типа используйте JsonSerializableAttribute.GenerationMode свойство.
Пример режима оптимизации сериализации (быстрый путь)
Для всего контекста:
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { }
Для отдельного типа:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { }
Полный пример программы
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"} } } }
Пример режима на основе метаданных
Для всего контекста:
[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);
Для отдельного типа:
[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);
Полный пример программы
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"} } } }
Поддержка создания источников в ASP.NET Core
В приложениях Blazor используйте перегрузки HttpClientJsonExtensions.GetFromJsonAsync методов расширения, HttpClientJsonExtensions.PostAsJsonAsync которые принимают контекст создания источника или TypeInfo<TValue>
.
Начиная с .NET 8, можно также использовать перегрузки HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable методов расширения, которые принимают контекст создания источника или TypeInfo<TValue>
.
В приложениях Razor Pages, MVC, SignalR и веб-API используйте JsonSerializerOptions.TypeInfoResolver свойство для указания контекста.
[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));
Примечание.
JsonSourceGenerationMode.SerializationСериализация с быстрым путем не поддерживается для асинхронной сериализации.
В .NET 7 и более ранних версиях это ограничение также применяется к синхронным перегрузкам JsonSerializer.Serialize , которые принимают Stream. Начиная с .NET 8, несмотря на то, что потоковая сериализация требует моделей на основе метаданных, она вернется к быстрому пути, если полезные данные, как известно, достаточно малы, чтобы соответствовать заданному размеру буфера. Дополнительные сведения см. в разделе https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json.
Отключение значений по умолчанию для отражения
Так как System.Text.Json использует отражение по умолчанию, вызов базового метода сериализации может нарушить собственные приложения AOT, которые не поддерживают все необходимые API отражения. Эти разрывы могут быть сложными для диагностики, так как они могут быть непредсказуемыми, и приложения часто отлаживаются с помощью среды выполнения CoreCLR, где работает отражение. Вместо этого при явной отключении сериализации на основе отражения разрывы проще диагностировать. Код, использующий сериализацию на основе отражения, приведет InvalidOperationException к возникновению описательного сообщения во время выполнения.
Чтобы отключить отражение по умолчанию в приложении, задайте JsonSerializerIsReflectionEnabledByDefault
для свойства MSBuild значение false
в файле проекта:
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
- Поведение этого свойства согласовано независимо от среды выполнения, CoreCLR или Native AOT.
- Если вы не указываете это свойство и параметр PublishTrimmed включен, сериализация на основе отражения автоматически отключается.
Вы можете программно проверить, отключается ли отражение с помощью JsonSerializer.IsReflectionEnabledByDefault свойства. В следующем фрагменте кода показано, как настроить сериализатор в зависимости от того, включена ли отражение:
static JsonSerializerOptions CreateDefaultOptions()
{
return new()
{
TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
? new DefaultJsonTypeInfoResolver()
: MyContext.Default
};
}
Так как свойство рассматривается как константа времени связи, предыдущий метод не корень сопоставителя на основе отражения в приложениях, работающих в машинном AOT.
Указание параметров
В .NET 8 и более поздних версиях большинство параметров, которые можно задать с помощью JsonSerializerOptions атрибута JsonSourceGenerationOptionsAttribute , можно также задать. Преимущество настройки параметров через атрибут заключается в том, что конфигурация указана во время компиляции, которая гарантирует, что созданное MyContext.Default
свойство предварительно настроено со всеми соответствующими наборами параметров.
В следующем коде показано, как задать параметры с помощью атрибута JsonSourceGenerationOptionsAttribute .
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
При использовании JsonSourceGenerationOptionsAttribute
для указания параметров сериализации вызовите один из следующих методов сериализации:
Метод
JsonSerializer.Serialize
, который принимаетTypeInfo<TValue>
. Передайте свойствоDefault.<TypeName>
класса контекста:jsonString = JsonSerializer.Serialize( weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
JsonSerializer.Serialize
Метод, который принимает контекст.Default
Передайте статическое свойство класса контекста.jsonString = JsonSerializer.Serialize( weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
При вызове метода, который позволяет передавать собственный экземпляр Utf8JsonWriter
, параметр записи Indented учитывается вместо JsonSourceGenerationOptionsAttribute.WriteIndented
параметра.
При создании и использовании экземпляра контекста путем вызова конструктора, принимающего JsonSerializerOptions
экземпляр, указанный экземпляр будет использоваться вместо параметров, указанных в параметре JsonSourceGenerationOptionsAttribute
.
Ниже приведены приведенные выше примеры в полной программе:
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"
//}
}
}
}
Объединение генераторов источников
Вы можете объединить контракты из нескольких исходных контекстов внутри одного JsonSerializerOptions экземпляра. JsonSerializerOptions.TypeInfoResolver Используйте свойство для цепочки нескольких контекстов, объединенных с помощью JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]) метода.
var options = new JsonSerializerOptions
{
TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default);
};
Начиная с .NET 8, если позже вы хотите добавить или добавить другой контекст, это можно сделать с помощью JsonSerializerOptions.TypeInfoResolverChain свойства. Порядок цепочки имеет важное значение: JsonSerializerOptions запрашивает каждый из сопоставителей в указанном порядке и возвращает первый результат, который не имеет значения 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.
Любые изменения, TypeInfoResolverChain внесенные в свойство, отражаются TypeInfoResolver и наоборот.
Сериализация полей перечисления в виде строк
По умолчанию перечисления сериализуются как числа. Чтобы сериализовать поля конкретного перечисления в виде строк при использовании создания источника, заметите его с JsonStringEnumConverter<TEnum> помощью преобразователя. Или, чтобы задать политику одеяла для всех перечислений, используйте JsonSourceGenerationOptionsAttribute атрибут.
JsonStringEnumConverter<T>
преобразователь
Чтобы сериализовать имена перечислений в виде строк с помощью создания источника, используйте преобразователь JsonStringEnumConverter<TEnum> . (Не универсальный JsonStringEnumConverter тип не поддерживается средой выполнения AOT в машинном коде.)
Заметите тип перечисления с JsonStringEnumConverter<TEnum> помощью преобразователя с помощью атрибута 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
}
JsonSerializerContext Создайте класс и заметите его атрибутомJsonSerializableAttribute:
[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }
Следующий код сериализует имена перечислений вместо числовых значений:
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);
Итоговый код JSON выглядит следующим образом:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}
Политика одеяла
Вместо использования JsonStringEnumConverter<TEnum> типа можно применить политику одеяла для сериализации перечислений в виде строк с помощью .JsonSourceGenerationOptionsAttribute JsonSerializerContext Создайте класс и заметите его с JsonSerializableAttribute помощью атрибутов:JsonSourceGenerationOptionsAttribute
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }
Обратите внимание, что перечисление не имеет :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
}
Пользовательские имена элементов перечисления
Начиная с .NET 9, можно настроить имена членов перечисления с помощью атрибута JsonStringEnumMemberName. Дополнительные сведения см. в разделе "Пользовательские имена элементов перечисления".