System.Text.Json 源生成器回退

使用接受 JsonSerializerOptionsJsonSerializer 方法之一时,System.Text.Json 源生成器将不再隐式回退到针对无法识别的类型基于反射的序列化。

旧行为

请查看 .NET 6 中的以下源生成示例:

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

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

public class Poco1 { }
public class Poco2 { }

由于 MyContext 的可序列化类型中不包含 Poco2,因此序列化将失败,并出现以下异常:

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.

现在,请查看以下调用,该调用尝试使用源生成器构造的 JsonSerializerOptions 实例序列化同一类型 (MyContext):

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

options 实例以无提示方式融入了基于反射的默认协定解析程序作为一种回退机制,因此,该类型使用反射成功进行序列化。

对于附加到 JsonSerializerContext 的 options 实例,相同的回退逻辑也适用于 JsonSerializerOptions.GetConverter(Type)。 以下语句将使用内置反射转换器返回一个转换器:

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

新行为

从 .NET 7 开始,以下调用将失败,并出现与使用 JsonSerializerContext 重载时相同的异常 (InvalidOperationException):

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

此外,以下语句将失败并显示 NotSupportedException

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

引入的版本

.NET 7

中断性变更的类型

此项更改可能会影响二进制兼容性

更改原因

之前的行为违反了“意外最少原则”,最终未实现源生成的目的。 在发布了一项使你能够自定义你的类型的 JSON 序列化协定的功能后,你可以调整合同元数据的源。 考虑到这一点,以无提示方式引入替代源变得更加不可取。

你可能会有意或无意地依赖于之前的行为。 建议的操作过程是更新 JsonSerializerContext 定义,以使其包括所有类型依赖项:

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

这样,应用程序就可以充分利用源生成的优势,包括剪裁安全性。

但是,在某些情况下,进行此类更改可能不切实际或不可能。 尽管不建议这样做,但可以通过多种方式在源生成的序列化程序中重新启用反射回退。

使用自定义协定解析程序

可以使用新的协定自定义功能来生成一个自定义协定解析程序,该解析程序回退到基于反射的解析(如果需要):

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.

使用 AppContext 开关

从 .NET 7 开始,可以使用提供的 AppContext 兼容性开关在全局范围内重新启用反射回退。 将以下条目添加到应用程序的项目文件中,以便为应用中所有源生成的上下文重新启用反射回退。 有关使用 AppContext 开关的详细信息,请参阅有关 .NET 运行时配置设置的文章。

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

受影响的 API