System.Text.Json 源生成器回退

使用接受 JsonSerializerJsonSerializerOptions 方法之一时,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.

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

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

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

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

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

新行为

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

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