Compartir vía


Uso de la generación de origen en System.Text.Json

La generación de origen de System.Text.Json está disponible en .NET 6 y versiones posteriores. Cuando se usa en una aplicación, la versión del lenguaje de la aplicación debe ser C# 9.0 o posterior. En este artículo se muestra cómo usar la serialización respaldada por la generación de origen en las aplicaciones.

Para obtener información sobre los diferentes modos de generación de origen, consulte Modos de generación de origen.

Uso de los valores predeterminados de la generación de origen

Para usar la generación de origen con todos los valores predeterminados (ambos modos, opciones predeterminadas):

  1. Cree una clase que derive de JsonSerializerContext.

  2. Especifique el tipo que se va a serializar o deserializar aplicando JsonSerializableAttribute a la clase de contexto.

  3. Llame a un método JsonSerializer que haga una de las siguientes acciones:

De forma predeterminada, se usan ambos modos de generación de origen si no se especifica uno. Para obtener información sobre cómo especificar el modo que se va a usar, vea Especificar el modo de generación de origen más adelante en este artículo.

Este es el tipo que se usa en los ejemplos siguientes:

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
}

Esta es la clase de contexto configurada para realizar la generación de origen para la clase anterior WeatherForecast:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

No es necesario especificar explícitamente los tipos de miembros WeatherForecast con atributos [JsonSerializable]. Los miembros declarados como object son una excepción a esta regla. El tipo de runtime de un miembro declarado como object debe especificarse. Por ejemplo, supongamos que tiene la siguiente clase:

public class WeatherForecast
{
    public object? Data { get; set; }
    public List<object>? DataList { get; set; }
}

Y sabe que en tiempo de ejecución puede tener objetos boolean y int:

WeatherForecast wf = new() { Data = true, DataList = new List<object> { true, 1 } };

A continuación, boolean y int deben declararse como [JsonSerializable]:

[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}

Para especificar la generación de origen para una colección, use [JsonSerializable] con el tipo de colección . Por ejemplo: [JsonSerializable(typeof(List<WeatherForecast>))].

Los métodos JsonSerializer que usan la generación de origen

En los ejemplos anteriores, la propiedad Default estática del tipo de contexto proporciona una instancia del tipo de contexto con opciones predeterminadas. La instancia de contexto proporciona una propiedad WeatherForecast que devuelve una instancia JsonTypeInfo<WeatherForecast>. Puede especificar un nombre diferente para esta propiedad mediante la propiedad TypeInfoPropertyName del atributo [JsonSerializable].

Ejemplos de serialización

Usar JsonTypeInfo<T>:

jsonString = JsonSerializer.Serialize(
    weatherForecast!, SourceGenerationContext.Default.WeatherForecast);

Usar JsonSerializerContext:

jsonString = JsonSerializer.Serialize(
    weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);

Usar JsonSerializerOptions:

sourceGenOptions = new JsonSerializerOptions
{
    TypeInfoResolver = SourceGenerationContext.Default
};

jsonString = JsonSerializer.Serialize(
    weatherForecast, typeof(WeatherForecast), sourceGenOptions);

Ejemplos de deserialización

Usar JsonTypeInfo<T>:

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
    jsonString, SourceGenerationContext.Default.WeatherForecast);

Usar JsonSerializerContext:

weatherForecast = JsonSerializer.Deserialize(
    jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
    as WeatherForecast;

Usar JsonSerializerOptions:

var sourceGenOptions = new JsonSerializerOptions
{
    TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
    jsonString, typeof(WeatherForecast), sourceGenOptions)
    as WeatherForecast;

Ejemplo completo del programa

Estos son los ejemplos anteriores en un 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 el modo de generación de origen

Se puede especificar el modo basado en metadatos o el modo de optimización de serialización para un contexto completo, que puede incluir varios tipos. O bien, puede especificar el modo para un tipo individual. Si lo hace, la especificación del modo para un tipo gana.

Ejemplo del modo de optimización de serialización (ruta de acceso rápida)

  • Para un contexto completo:

    [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
    [JsonSerializable(typeof(WeatherForecast))]
    internal partial class SerializeOnlyContext : JsonSerializerContext
    {
    }
    
  • Para un tipo individual:

    [JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)]
    internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext
    {
    }
    
  • Ejemplo completo del programa

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

Ejemplo del modo basado en metadatos

  • Para un contexto completo:

    [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 un 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);
    
  • Ejemplo completo del programa

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

Compatibilidad con la generación de origen en ASP.NET Core

En las aplicaciones Blazor, use las sobrecargas de los métodos de extensión HttpClientJsonExtensions.GetFromJsonAsync y HttpClientJsonExtensions.PostAsJsonAsync que toman un contexto de generación de origen o TypeInfo<TValue>.

A partir de .NET 8, también es posible usar sobrecargas de métodos de extensión HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable que acepten un contexto de generación de origen o TypeInfo<TValue>.

En Razor Pages, MVC, SignalR y las aplicaciones de API web, use la propiedad JsonSerializerOptions.TypeInfoResolver para especificar el 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));

En Razor Pages, MVC, SignalR y las aplicaciones de API web, use la propiedad JsonSerializerOptions.TypeInfoResolver para especificar el contexto.

[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }
var serializerOptions = new JsonSerializerOptions
{
    TypeInfoResolver = MyJsonContext.Default
};

services.AddControllers().AddJsonOptions(
    static options =>
        options.JsonSerializerOptions = serializerOptions);

En Razor Pages, MVC, SignalR y las aplicaciones de API web, use el método AddContext de JsonSerializerOptions, tal y como se muestra en el ejemplo siguiente:

[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }
services.AddControllers().AddJsonOptions(options =>
    options.JsonSerializerOptions.AddContext<MyJsonContext>());

Nota:

JsonSourceGenerationMode.Serialization o la serialización de rutas rápidas no se admite para la serialización asincrónica.

En .NET 7 y versiones anteriores, esta limitación también se aplica a las sobrecargas sincrónicas de JsonSerializer.Serialize que aceptan Stream. A partir de .NET 8, aunque la serialización de streaming requiere modelos basados en metadatos, se revertirá a la ruta de acceso rápida si se sabe que las cargas son lo suficientemente pequeñas como para ajustarse al tamaño de búfer predeterminado. Para obtener más información, vea https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json.

Deshabilitar los valores predeterminados de reflexión

Dado que System.Text.Json usa la reflexión de forma predeterminada, llamar a un método de serialización básico puede interrumpir las aplicaciones AOT nativas, lo que no admite todas las API de reflexión necesarias. Estas interrupciones pueden ser difíciles de diagnosticar, ya que pueden ser impredecibles y las aplicaciones a menudo se depuran mediante el runtime de CoreCLR, donde funciona la reflexión. En su lugar, si deshabilita explícitamente la serialización basada en reflexión, las interrupciones serán más fáciles de diagnosticar. El código que usa la serialización basada en reflexión hará que se produzca un InvalidOperationException con un mensaje descriptivo mostrado en tiempo de ejecución.

Para deshabilitar la reflexión predeterminada en la aplicación, establezca la propiedad MSBuild JsonSerializerIsReflectionEnabledByDefault en false en el archivo del proyecto:

<PropertyGroup>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
  • El comportamiento de esta propiedad es coherente, independientemente del runtime, ya sea CoreCLR o AOT nativo.
  • Si no se especifica esta propiedad y PublishTrimmed estuviera habilitado, la serialización basada en reflexión se deshabilitará automáticamente.

Es posible comprobar mediante programación si la reflexión está deshabilitada gracias a la propiedad JsonSerializer.IsReflectionEnabledByDefault. El siguiente fragmento de código muestra cómo se configura el serializador en función de si la reflexión está habilitada:

static JsonSerializerOptions CreateDefaultOptions()
{
    return new()
    {
        TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
            ? new DefaultJsonTypeInfoResolver()
            : MyContext.Default
    };
}

Dado que la propiedad se trata como una constante en tiempo de vínculo, el método anterior no enraíza el solucionador basado en reflexión en las aplicaciones que se ejecuten en AOT nativo.

Especificar opciones

En .NET 8 y versiones posteriores, la mayoría de las opciones que se pueden establecer mediante JsonSerializerOptions también se pueden establecer con el atributo JsonSourceGenerationOptionsAttribute. La ventaja de establecer las opciones a través del atributo es que la configuración se especifica en tiempo de compilación, lo que garantizará que la propiedad MyContext.Default generada esté preconfigurada con todos los conjuntos de opciones pertinentes.

El siguiente código muestra cómo establecer opciones utilizando el atributo JsonSourceGenerationOptionsAttribute.

[JsonSourceGenerationOptions(
    WriteIndented = true,
    PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
    GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}

Al usar JsonSourceGenerationOptionsAttribute para especificar opciones de serialización, llame a uno de los métodos de serialización siguientes:

  • Un método JsonSerializer.Serialize que toma un TypeInfo<TValue>. Pase la propiedad Default.<TypeName> de la clase de contexto:

    jsonString = JsonSerializer.Serialize(
        weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
    
  • Un método JsonSerializer.Serialize que toma un contexto. Pase la propiedad estática Default de la clase de contexto.

    jsonString = JsonSerializer.Serialize(
        weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
    

Si llama a un método que le permite pasar su propia instancia de Utf8JsonWriter, se respeta la configuración del escritor Indented en lugar de la opción JsonSourceGenerationOptionsAttribute.WriteIndented.

Si crea y usa una instancia de contexto llamando al constructor que toma una instancia JsonSerializerOptions, se usará la instancia proporcionada en lugar de las opciones especificadas por JsonSourceGenerationOptionsAttribute.

Estos son los ejemplos anteriores en un 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"
            //}
        }
    }
}

Especificar opciones mediante JsonSerializerOptions

Algunas opciones de JsonSerializerOptions no se pueden establecer mediante JsonSourceGenerationOptionsAttribute. Especificar opciones mediante JsonSerializerOptions:

  • Crea una instancia de JsonSerializerOptions.
  • Cree una instancia de la clase que derive de JsonSerializerContext y pase la instancia JsonSerializerOptions al constructor.
  • Llama a métodos de serialización o deserialización de JsonSerializer que toman una instancia de contexto o TypeInfo<TValue>.

Esta es una clase de contexto de ejemplo seguida de código de ejemplo de serialización y deserialización:

[JsonSerializable(typeof(WeatherForecast))]
internal partial class OptionsExampleContext : JsonSerializerContext
{
}
jsonString = JsonSerializer.Serialize(
    weatherForecast,
    typeof(WeatherForecast),
    new OptionsExampleContext(
        JsonSerializerOptions.Web));
weatherForecast = JsonSerializer.Deserialize(
    jsonString,
    typeof(WeatherForecast),
    new OptionsExampleContext(
        JsonSerializerOptions.Web))
        as WeatherForecast;

Estos son los ejemplos anteriores en un programa completo:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonSerializerOptionsExample
{
    public class WeatherForecast
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
    }

    [JsonSerializable(typeof(WeatherForecast))]
    internal partial class OptionsExampleContext : 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(
                jsonString,
                typeof(WeatherForecast),
                new OptionsExampleContext(
                    JsonSerializerOptions.Web))
                    as WeatherForecast;
            Console.WriteLine($"Date={weatherForecast?.Date}");
            // output:
            //Date=8/1/2019 12:00:00 AM

            jsonString = JsonSerializer.Serialize(
                weatherForecast,
                typeof(WeatherForecast),
                new OptionsExampleContext(
                    JsonSerializerOptions.Web));
            Console.WriteLine(jsonString);
            // output:
            //{ "date":"2019-08-01T00:00:00","temperatureCelsius":25,"summary":"Hot"}
        }
    }
}

Combinación de generadores de origen

Es posible combinar contratos de varios contextos generados por el origen dentro de una sola instancia JsonSerializerOptions. Use la propiedad JsonSerializerOptions.TypeInfoResolver para encadenar varios contextos que se hayan combinado mediante el método JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]).

var options = new JsonSerializerOptions
{
    TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default);
};

A partir de .NET 8, si después desea anteponer o anexar otro contexto, es posible hacerlo mediante la propiedad JsonSerializerOptions.TypeInfoResolverChain. El orden de la cadena es significativo: JsonSerializerOptions consulta cada uno de los solucionadores en el orden especificado y devuelve el primer resultado que no sea 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.

Cualquier cambio realizado en la propiedad TypeInfoResolverChain se refleja por TypeInfoResolver y viceversa.

Serializar campos de enumeración como cadenas

De forma predeterminada, las enumeraciones se serializan como números. Para serializar los campos de una enumeración específica como cadenas al usar la generación de origen, anótela con el convertidor JsonStringEnumConverter<TEnum>. O bien, para establecer una directiva general para todas las enumeraciones, use el atributo JsonSourceGenerationOptionsAttribute.

Convertidor de JsonStringEnumConverter<T>

Para serializar los nombres de enumeración como cadenas mediante la generación de origen, use el convertidor JsonStringEnumConverter<TEnum>. (El tipo no genérico JsonStringEnumConverter no es compatible con el entorno de ejecución de AOT nativo).

Anote el tipo de enumeración con el convertidor JsonStringEnumConverter<TEnum> mediante el atributo 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
}

Cree una clase JsonSerializerContext y anótela con el atributo JsonSerializableAttribute:

[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }

El código siguiente serializa los nombres de enumeración en lugar de los 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);

El JSON resultante tendrá un aspecto similar al siguiente:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Precipitation": "Sleet"
}

Directiva general

En lugar de usar el tipo JsonStringEnumConverter<TEnum>, puede aplicar una directiva general para serializar enumeraciones como cadenas mediante el JsonSourceGenerationOptionsAttribute. Cree una clase JsonSerializerContext y anótela con los atributos JsonSerializableAttribute y JsonSourceGenerationOptionsAttribute:

[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }

Observe que la enumeración no tiene el 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
}

Nombres de miembro de enumeración personalizados

A partir de .NET 9, puede personalizar los nombres de miembro de enumeración mediante el atributo JsonStringEnumMemberName. Para obtener más información, consulte Nombres de miembro de enumeración personalizados.

Consulte también