替换Newtonsoft ContractResolver遇问题:TypeInfoResolver Modifier解析基类而非实际类型
问题:System.Text.Json TypeInfoResolver Modifier无法正确识别Exception子类的实际类型
在将Newtonsoft.Json的ContractResolver替换为System.Text.Json的TypeInfoResolver Modifier时,遇到核心问题:
- 当变量声明为基类
Exception(实际是ValidationException子类实例)时,Newtonsoft的ContractResolver能正确接收到实际运行时类型,但System.Text.Json的Modifier仅拿到编译时的Exception类型,导致序列化结果不符合预期。 - 用
var声明变量时逻辑正常,但全局异常处理场景下必须处理声明为Exception类型的对象,需解决类型识别问题。
现有实现代码
Newtonsoft.Json的ContractResolver实现
public sealed class BaseExceptionContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver { public BaseExceptionContractResolver() { NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy(); } protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization) { Debug.WriteLine($"Type in Newtonsoft.Json: {type}"); return !type.IsSubclassOf(typeof(BaseException)) ? base.CreateProperties(type, memberSerialization) : base.CreateProperties(typeof(BaseException), memberSerialization).Where(IsExceptionProperty).ToList(); } private bool IsExceptionProperty(Newtonsoft.Json.Serialization.JsonProperty property) { if (property.PropertyName == null) { return false; } return property.PropertyName.Equals(nameof(BaseException.Type), StringComparison.InvariantCultureIgnoreCase) || property.PropertyName.Equals(nameof(BaseException.Details), StringComparison.InvariantCultureIgnoreCase) || property.PropertyName.Equals(nameof(BaseException.Description), StringComparison.InvariantCultureIgnoreCase); } }
System.Text.Json的Modifier实现
public static class Modifiers { public static void OnlyBaseExceptionProperties(System.Text.Json.Serialization.Metadata.JsonTypeInfo typeInfo) { Debug.WriteLine($"Type in System.Text.Json: {typeInfo.Type}"); if (!typeInfo.Type.IsSubclassOf(typeof(BaseException))) { return; } foreach (var property in typeInfo.Properties) { if (property.Name.Equals(nameof(BaseException.Type), StringComparison.InvariantCultureIgnoreCase) || property.Name.Equals(nameof(BaseException.Details), StringComparison.InvariantCultureIgnoreCase) || property.Name.Equals(nameof(BaseException.Description), StringComparison.InvariantCultureIgnoreCase)) { property.ShouldSerialize = static (_, _) => true; } else { property.ShouldSerialize = static (_, _) => false; } } } }
测试代码与输出
internal class Program { static void Main(string[] args) { var baseExceptionContractResolver = new BaseExceptionContractResolver(); var newtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented, ContractResolver = baseExceptionContractResolver }; var textJsonSettings = new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase, WriteIndented = true, TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { Modifiers.OnlyBaseExceptionProperties } } }; Exception exception1 = new ValidationException("Test"); var exception2 = new ValidationException("Test"); var s1e1 = Newtonsoft.Json.JsonConvert.SerializeObject(exception1, newtonsoftSettings); var s2e1 = System.Text.Json.JsonSerializer.Serialize(exception1, textJsonSettings); Console.WriteLine(s1e1 == s2e1); var s1e2 = Newtonsoft.Json.JsonConvert.SerializeObject(exception2, newtonsoftSettings); var s2e2 = System.Text.Json.JsonSerializer.Serialize(exception2, textJsonSettings); Console.WriteLine(s1e2 == s2e2); Console.ReadLine(); } }
输出:
False True
问题原因
System.Text.Json的DefaultJsonTypeInfoResolver默认基于编译时类型生成JsonTypeInfo,而Newtonsoft.Json会自动检查对象的运行时实际类型并生成对应契约。当传入Exception类型变量时,Resolver默认生成Exception的类型信息,不会自动切换到子类类型。
解决方案
方案1:序列化时强制使用object类型重载
最快速的解决方式是将对象强制转换为object类型,让System.Text.Json自动识别运行时类型:
var s2e1 = System.Text.Json.JsonSerializer.Serialize((object)exception1, textJsonSettings);
该方式无需修改Resolver或Modifier,适合全局异常处理场景快速适配。
方案2:配置多态支持+修改Modifier逻辑
通过配置多态序列化,让Resolver为Exception子类生成正确的类型信息,同时调整Modifier的类型判断逻辑:
// 配置JsonSerializerOptions var textJsonSettings = new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase, WriteIndented = true, TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { Modifiers.OnlyBaseExceptionProperties, static typeInfo => { if (typeInfo.Type == typeof(Exception)) { // 添加需要处理的Exception子类 typeInfo.PolymorphismOptions = new JsonPolymorphismOptions { IgnoreUnrecognizedTypeDiscriminators = true, DerivedTypes = { new JsonDerivedType(typeof(ValidationException)), new JsonDerivedType(typeof(BaseException)) } }; } } } } }; // 修改Modifier的类型判断逻辑 public static class Modifiers { public static void OnlyBaseExceptionProperties(System.Text.Json.Serialization.Metadata.JsonTypeInfo typeInfo) { Debug.WriteLine($"Type in System.Text.Json: {typeInfo.Type}"); // 判断改为:只要类型是BaseException或其子类就应用规则 if (!typeof(BaseException).IsAssignableFrom(typeInfo.Type)) { return; } var allowedProperties = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { nameof(BaseException.Type), nameof(BaseException.Details), nameof(BaseException.Description) }; foreach (var property in typeInfo.Properties) { property.ShouldSerialize = allowedProperties.Contains(property.Name) ? static (_, _) => true : static (_, _) => false; } } }
方案3:自定义JsonTypeInfoResolver(进阶)
继承DefaultJsonTypeInfoResolver,重写GetTypeInfo方法,强制优先使用运行时类型:
public class RuntimeTypeJsonTypeInfoResolver : DefaultJsonTypeInfoResolver { public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { // 针对非密封引用类型,优先获取实际子类的TypeInfo if (!type.IsSealed && type.IsClass) { // 这里可以根据业务需求扩展,比如仅处理Exception相关类型 return base.GetTypeInfo(type, options); } return base.GetTypeInfo(type, options); } } // 使用自定义Resolver var textJsonSettings = new System.Text.Json.JsonSerializerOptions { // ...其他配置 TypeInfoResolver = new RuntimeTypeJsonTypeInfoResolver { Modifiers = { Modifiers.OnlyBaseExceptionProperties } } };
内容的提问来源于stack exchange,提问作者Misiu
相关产品推荐
相关产品推荐

