Como usar a geração de fontes em System.Text.Json
A geração de código-fonte está System.Text.Json disponível no .NET 6 e versões posteriores. Quando usado em um aplicativo, a versão de idioma do aplicativo deve ser C# 9.0 ou posterior. Este artigo mostra como usar a serialização com suporte de geração de origem em seus aplicativos.
Para obter informações sobre os diferentes modos de geração de origem, consulte Modos de geração de origem.
Usar padrões de geração de origem
Para usar a geração de origem com todos os padrões (ambos os modos, opções padrão):
Crie uma classe parcial que deriva de JsonSerializerContext.
Especifique o tipo a ser serializado ou desserializado aplicando JsonSerializableAttribute à classe de contexto.
Chame um JsonSerializer método que:
- Toma um JsonTypeInfo<T> exemplo, ou
- Toma um JsonSerializerContext exemplo, ou
- Usa uma JsonSerializerOptions instância e você define sua JsonSerializerOptions.TypeInfoResolver propriedade como a
Default
propriedade do tipo de contexto (somente .NET 7 e posterior).
Por padrão, ambos os modos de geração de origem são usados se você não especificar um. Para obter informações sobre como especificar o modo a ser usado, consulte Especificar modo de geração de origem mais adiante neste artigo.
Aqui está o tipo usado nos seguintes exemplos:
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
Aqui está a classe de contexto configurada para fazer a geração de origem para a classe anterior WeatherForecast
:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
Os tipos de WeatherForecast
membros não precisam ser explicitamente especificados com [JsonSerializable]
atributos. Os membros declarados como object
exceções a esta regra. O tipo de tempo de execução para um membro declarado como object
precisa ser especificado. Por exemplo, suponha que você tenha a seguinte classe:
public class WeatherForecast
{
public object? Data { get; set; }
public List<object>? DataList { get; set; }
}
E você sabe que em tempo de execução ele pode ter boolean
e int
objetos:
WeatherForecast wf = new() { Data = true, DataList = new List<object> { true, 1 } };
Em seguida, boolean
e int
tem de ser declarado como [JsonSerializable]
:
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}
Para especificar a geração de origem para uma coleção, use [JsonSerializable]
com o tipo de coleção. Por exemplo: [JsonSerializable(typeof(List<WeatherForecast>))]
.
JsonSerializer
Métodos que usam a geração de origem
Nos exemplos a seguir, a propriedade static Default
do tipo de contexto fornece uma instância do tipo de contexto com opções padrão. A instância de contexto fornece uma WeatherForecast
propriedade que retorna uma JsonTypeInfo<WeatherForecast>
instância. Você pode especificar um nome diferente para essa propriedade usando a TypeInfoPropertyName [JsonSerializable]
propriedade do atributo.
Exemplos de serialização
Utilização de JsonTypeInfo<T>:
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
Utilização de JsonSerializerContext:
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
Utilização de JsonSerializerOptions:
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);
Exemplos de desserialização
Utilização de JsonTypeInfo<T>:
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, SourceGenerationContext.Default.WeatherForecast);
Utilização de JsonSerializerContext:
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
Utilização de JsonSerializerOptions:
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;
Exemplo de programa completo
Aqui estão os exemplos anteriores em um programa completo:
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"}
}
}
}
Especificar o modo de geração de origem
Você pode especificar o modo baseado em metadados ou o modo de otimização de serialização para um contexto inteiro, que pode incluir vários tipos. Ou você pode especificar o modo para um tipo individual. Se você fizer as duas coisas, a especificação de modo para um tipo vence.
- Para um contexto inteiro, use a JsonSourceGenerationOptionsAttribute.GenerationMode propriedade.
- Para um tipo individual, use a JsonSerializableAttribute.GenerationMode propriedade.
Exemplo do modo de otimização de serialização (caminho rápido)
Para todo um contexto:
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { }
Para um tipo individual:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { }
Exemplo de programa completo
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"} } } }
Exemplo de modo baseado em metadados
Para todo um contexto:
[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);
Para um tipo individual:
[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);
Exemplo de programa completo
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"} } } }
Suporte à geração de origem no ASP.NET Core
Em aplicativos Blazor, use sobrecargas e HttpClientJsonExtensions.PostAsJsonAsync métodos de extensão que tomam um contexto de geração de HttpClientJsonExtensions.GetFromJsonAsync origem ou TypeInfo<TValue>
.
A partir do .NET 8, você também pode usar sobrecargas de métodos de extensão que aceitam um contexto de geração de HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable origem ou TypeInfo<TValue>
.
Em aplicativos Razor Pages, MVC, SignalR e API Web, use a JsonSerializerOptions.TypeInfoResolver propriedade para especificar o contexto.
[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));
Nota
JsonSourceGenerationMode.Serializationou serialização de caminho rápido, não é suportada para serialização assíncrona.
No .NET 7 e versões anteriores, essa limitação também se aplica a sobrecargas síncronas que JsonSerializer.Serialize aceitam um Streamarquivo . A partir do .NET 8, embora a serialização de streaming exija modelos baseados em metadados, ela voltará ao caminho rápido se as cargas úteis forem conhecidas por serem pequenas o suficiente para caber no tamanho predeterminado do buffer. Para obter mais informações, veja https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json.
Desativar padrões de reflexão
Como System.Text.Json usa reflexão por padrão, chamar um método de serialização básico pode quebrar aplicativos AOT nativos, que não suportam todas as APIs de reflexão necessárias. Essas quebras podem ser difíceis de diagnosticar, uma vez que podem ser imprevisíveis, e os aplicativos geralmente são depurados usando o tempo de execução CoreCLR, onde a reflexão funciona. Em vez disso, se você desabilitar explicitamente a serialização baseada em reflexão, as quebras serão mais fáceis de diagnosticar. O código que usa a serialização baseada em reflexão fará com que uma InvalidOperationException mensagem com uma descritiva seja lançada em tempo de execução.
Para desabilitar o reflexo padrão em seu aplicativo, defina a JsonSerializerIsReflectionEnabledByDefault
propriedade MSBuild como false
em seu arquivo de projeto:
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
- O comportamento dessa propriedade é consistente independentemente do tempo de execução, CoreCLR ou Native AOT.
- Se você não especificar essa propriedade e PublishTrimmed estiver habilitado, a serialização baseada em reflexão será automaticamente desabilitada.
Você pode verificar programaticamente se a reflexão está desabilitada usando a JsonSerializer.IsReflectionEnabledByDefault propriedade. O trecho de código a seguir mostra como você pode configurar seu serializador dependendo se a reflexão está habilitada:
static JsonSerializerOptions CreateDefaultOptions()
{
return new()
{
TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
? new DefaultJsonTypeInfoResolver()
: MyContext.Default
};
}
Como a propriedade é tratada como uma constante de tempo de link, o método anterior não enraíza o resolvedor baseado em reflexão em aplicativos executados na AOT nativa.
Especificar opções
No .NET 8 e versões posteriores, a maioria das opções que você pode definir usando JsonSerializerOptions também pode ser definida usando o JsonSourceGenerationOptionsAttribute atributo. A vantagem de definir opções através do atributo é que a configuração é especificada em tempo de compilação, o que garante que a propriedade gerada MyContext.Default
seja pré-configurada com todas as opções relevantes definidas.
O código a seguir mostra como definir opções usando o JsonSourceGenerationOptionsAttribute atributo.
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
Ao usar JsonSourceGenerationOptionsAttribute
para especificar opções de serialização, chame um dos seguintes métodos de serialização:
Um
JsonSerializer.Serialize
método que usa umTypeInfo<TValue>
arquivo . Passe-lhe aDefault.<TypeName>
propriedade da sua classe de contexto:jsonString = JsonSerializer.Serialize( weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
Um
JsonSerializer.Serialize
método que toma um contexto. Passe-lhe aDefault
propriedade estática da sua classe de contexto.jsonString = JsonSerializer.Serialize( weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
Se você chamar um método que permite que você passe em sua própria instância de , a configuração do Utf8JsonWriter
Indented escritor será honrada em vez da JsonSourceGenerationOptionsAttribute.WriteIndented
opção.
Se você criar e usar uma instância de contexto chamando o construtor que usa uma JsonSerializerOptions
instância, a instância fornecida será usada em vez das opções especificadas por JsonSourceGenerationOptionsAttribute
.
Aqui estão os exemplos anteriores em um programa completo:
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"
//}
}
}
}
Combine geradores de origem
Você pode combinar contratos de vários contextos gerados pela fonte dentro de uma única JsonSerializerOptions instância. Use a JsonSerializerOptions.TypeInfoResolver propriedade para encadear vários contextos que foram combinados usando o JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]) método.
var options = new JsonSerializerOptions
{
TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default);
};
A partir do .NET 8, se você quiser anexar ou anexar outro contexto, poderá fazê-lo usando a JsonSerializerOptions.TypeInfoResolverChain propriedade. A ordenação da cadeia é significativa: JsonSerializerOptions consulta cada um dos resolvedores em sua ordem especificada e retorna o primeiro resultado que não é nulo.
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.
Qualquer alteração feita na propriedade é refletida TypeInfoResolverChain por TypeInfoResolver e vice-versa.
Serializar campos enum como cadeias de caracteres
Por padrão, enums são serializados como números. Para serializar os campos de um enum específico como strings ao usar a geração de código-fonte, anote-o com o JsonStringEnumConverter<TEnum> conversor. Ou para definir uma política geral para todas as enumerações, use o JsonSourceGenerationOptionsAttribute atributo.
JsonStringEnumConverter<T>
conversor
Para serializar nomes de enum como cadeias de caracteres usando a geração de código-fonte, use o JsonStringEnumConverter<TEnum> conversor. (O tipo não genérico JsonStringEnumConverter não é suportado pelo tempo de execução nativo do AOT.)
Anote o tipo de enumeração com o JsonStringEnumConverter<TEnum> conversor usando o JsonConverterAttribute atributo:
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
}
Crie uma JsonSerializerContext classe e anote-a com o JsonSerializableAttribute atributo:
[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }
O código a seguir serializa os nomes de enum em vez dos valores numéricos:
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);
O JSON resultante se parece com o exemplo a seguir:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}
Política geral
Em vez de usar o JsonStringEnumConverter<TEnum> tipo, você pode aplicar uma política de cobertor para serializar enums como cadeias de caracteres usando o JsonSourceGenerationOptionsAttribute. Crie uma JsonSerializerContext classe e anote-a com os JsonSerializableAttribute atributos e JsonSourceGenerationOptionsAttribute :
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }
Observe que o enum não tem o 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
}
Nomes de membros enum personalizados
A partir do .NET 9, você pode personalizar nomes de membros enum usando o atributo JsonStringEnumMemberName. Para obter mais informações, consulte Nomes de membros enum personalizados.