Reversión del generador de código fuente System.Text.Json

Cuando se usa uno de los métodos JsonSerializer que acepta JsonSerializerOptions, el generador de código fuente System.Text.Json ya no volverá implícitamente a la serialización basada en reflexión para tipos no reconocidos.

Comportamiento anterior

Considere el ejemplo de generación de origen siguiente de .NET 6:

JsonSerializer.Serialize(new Poco2(), typeof(Poco2), MyContext.Default);

[JsonSerializable(typeof(Poco1))]
public partial class MyContext : JsonSerializerContext {}

public class Poco1 { }
public class Poco2 { }

Dado que MyContext no incluye Poco2 en sus tipos serializables, la serialización producirá un error como se espera con la siguiente excepción:

System.InvalidOperationException:

'Metadata for type 'Poco2' was not provided to the serializer. The serializer method used does not 
support reflection-based creation of serialization-related type metadata. If using source generation, 
ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', 
along with any types that might be serialized polymorphically.

Ahora considere la siguiente llamada, que intenta serializar el mismo tipo (MyContext) mediante la instancia de JsonSerializerOptions construida por el generador de código fuente:

JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);

La instancia de opciones incorpora de forma silenciosa el solucionador de contratos predeterminado basado en reflexión como mecanismo de reversión y, como tal, el tipo se serializa correctamente mediante la reflexión.

La misma lógica de reversión se aplica a JsonSerializerOptions.GetConverter(Type) para las instancias de opciones asociadas a JsonSerializerContext. La siguiente instrucción devolverá un convertidor mediante el convertidor de reflexión integrado:

JsonConverter converter = MyContext.Default.Options.GetConverter(typeof(Poco2));

Comportamiento nuevo

A partir de .NET 7, se produce un error en la siguiente llamada con la misma excepción (InvalidOperationException) que cuando se usa la sobrecarga JsonSerializerContext:

JsonSerializer.Serialize(new Poco2(), MyContext.Default.Options);

Además, se producirá un error en la siguiente instrucción con una excepción NotSupportedException:

JsonConverter converter = MyContext.Default.Options.GetConverter(typeof(Poco2));

Versión introducida

.NET 7

Tipo de cambio importante

Este cambio puede afectar a la compatibilidad binaria.

Motivo del cambio

El comportamiento anterior infringe el principio de menos sorpresa y, en última instancia, derrota el propósito de la generación de origen. Con el lanzamiento de una característica que le permite personalizar los contratos de serialización JSON de los tipos, tiene la posibilidad de ajustar los orígenes de los metadatos del contrato. Teniendo esto en cuenta, la introducción silenciosa de orígenes alternativos se vuelve aún menos deseable.

Es posible que dependa del comportamiento anterior, ya sea de manera intencionada o involuntariamente. El curso de acción recomendado es actualizar la definición de JsonSerializerContext para que incluya todas las dependencias de tipos:

[JsonSerializable(typeof(Poco1))]
[JsonSerializable(typeof(Poco2))]
public partial class MyContext : JsonSerializerContext {}

Esto permitirá que la aplicación aproveche al máximo las ventajas de la generación de origen, incluida la seguridad del recorte.

Sin embargo, en algunos casos, realizar este cambio podría no ser práctico o posible. Aunque no se recomienda, hay un par de formas de volver a habilitar la reversión de la reflexión en el serializador generado por el origen.

Uso de un solucionador de contratos personalizado

Puede usar la nueva característica de personalización de contrato para crear un solucionador de contratos personalizado que revierta a la resolución basada en reflexión cuando sea necesario:

var options = new JsonSerializerOptions
{
     TypeInfoResolver = JsonTypeInfoResolver.Combine(MyContext.Default, new DefaultJsonTypeInfoResolver());
}

JsonSerializer.Serialize(new Poco2(), options); // Contract resolution falls back to the default reflection-based resolver.
options.GetConverter(typeof(Poco2)); // Returns the reflection-based converter.

Uso de un modificador AppContext

A partir de .NET 7, puede volver a habilitar la reversión de reflexión globalmente mediante el modificador de compatibilidad AppContext proporcionado. Agregue la siguiente entrada al archivo de proyecto de la aplicación para volver a habilitar la reversión de reflexión para todos los contextos generados por el origen en la aplicación. Para más información sobre el uso de modificadores AppContext, consulte el artículo sobre la configuración del entorno de ejecución de .NET.

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Text.Json.Serialization.EnableSourceGenReflectionFallback" Value="true" />
</ItemGroup>

API afectadas