.NET 8.0 Blazor WASM迁移:序列化源码生成与多态问题咨询
背景
我们正将基于ASP.NET Core托管的现有Blazor WASM应用迁移至.NET 8.0,在裁剪(trimming)方面遇到诸多问题,其中之一是确保裁剪器不会移除Blazor WASM与Web API交互所用的类型。当前序列化/反序列化依赖基于反射的System.Text.Json且未启用裁剪,理论上迁移到源代码生成难度不大,但实际操作中遇到以下问题:
问题一:泛型类型的源代码生成序列化配置
项目中使用了如下泛型辅助类型:
public sealed record Descriptor<T>(T Value, string Description);
该类型用于API端点返回枚举值/字符串对,供Blazor WASM UI的下拉列表使用。尝试通过源代码生成序列化该类型时,需为派生自JsonSerializerContext的分部类添加JsonSerializableAttribute,但发现无法指定开放泛型类型,必须为每个闭合泛型(如Descriptor<Kind>、Descriptor<KindB>)添加属性;同时,若要序列化该类型的集合(如IEnumerable<Descriptor<Kind>>、List<Descriptor<Kind>>),也需单独添加对应的属性配置,否则无法正常序列化/反序列化。现咨询:
- 是否确实无法指定开放泛型类型?
- 是否必须通过
JsonSerializableAttribute指定所有待序列化/反序列化的类型(包括闭合泛型集合)?
解答
- 是的,当前(.NET 8)System.Text.Json的源代码生成器不支持直接配置开放泛型类型。源代码生成器需要明确的闭合泛型类型来生成针对性的序列化逻辑,因为开放泛型无法确定具体的字段/属性结构,无法提前生成代码。
- 对于闭合泛型的集合类型,不需要单独添加
JsonSerializableAttribute——只要你已经为Descriptor<Kind>添加了属性,源代码生成器会自动处理其集合类型(如List<Descriptor<Kind>>、IEnumerable<Descriptor<Kind>>)的序列化逻辑。但如果集合是嵌套的复杂泛型(比如List<Descriptor<Dictionary<string, int>>>),可能需要额外配置内层的闭合泛型类型。
问题二:自定义多态解析器与源代码生成的结合
我们没有Web API所用类型的源代码,API存在多态场景:控制器方法签名返回Base类型,但实际返回其派生类型。此前使用Json.NET序列化,迁移至System.Text.Json时,通过自定义DefaultJsonTypeInfoResolver实现了兼容Json.NET的多态鉴别器(属性名为$type,值为完整程序集限定名),代码如下:
public class JsonHierarchyTypeInfoResolver : DefaultJsonTypeInfoResolver { public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { var jsonTypeInfo = base.GetTypeInfo(type, options); var jsonType = jsonTypeInfo.Type; if (jsonType == typeof(Equipamento)) { jsonTypeInfo = GetTypeInfoForDevice(jsonTypeInfo); } // ... remaining code removed } private static JsonTypeInfo GetTypeInfoForDevice(JsonTypeInfo jsonTypeInfo) { jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions { TypeDiscriminatorPropertyName = "$type", IgnoreUnrecognizedTypeDiscriminators = true, UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization }; foreach (var jsonDerivedType in _derivedDevicesTypes) { jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add( new JsonDerivedType(jsonDerivedType, jsonDerivedType.AssemblyQualifiedName!)); } return jsonTypeInfo; } }
现咨询:
- 能否将该自定义解析器与源代码生成的解析器结合使用?还是源代码生成仅支持通过
JsonDerivedTypeAttribute实现多态? - 若无法结合,是否必须重写带有多态属性的DTO程序集,才能复用基于多态/继承的API端点?
解答
- 可以结合使用,但需要调整实现方式:
- 源代码生成器会生成一个继承自
JsonSerializerContext的类,该类内部使用的是SourceGenerationJsonSerializerContext的解析逻辑。你可以创建一个复合解析器,将源代码生成的解析器和自定义的JsonHierarchyTypeInfoResolver组合起来。 - 具体做法:在
JsonSerializerOptions中设置TypeInfoResolver为JsonTypeInfoResolver.Combine,传入源代码生成的上下文实例(如MyJsonSerializerContext.Default)和你的自定义解析器。这样系统会优先使用源代码生成的类型信息,对于未覆盖的类型(或需要多态处理的类型),再由自定义解析器补充处理。 - 注意:如果源代码生成的上下文已经包含了
Equipamento类型的配置,需要确保自定义解析器的逻辑能正确覆盖或补充其多态配置。
- 源代码生成器会生成一个继承自
- 不需要重写DTO程序集。除了使用
JsonDerivedTypeAttribute,你还可以:- 继续使用自定义的
JsonHierarchyTypeInfoResolver,同时为需要裁剪保护的类型添加JsonSerializableAttribute(确保源代码生成器包含这些类型,避免被裁剪)。 - 或者通过
JsonSerializerOptions的PolymorphismOptions手动配置多态规则,不需要修改DTO的源代码。如果结合源代码生成,只需要在上下文中标记基类为可序列化,然后在选项中补充多态配置即可。
- 继续使用自定义的
内容的提问来源于stack exchange,提问作者Luis Abreu

