自定义Stack Json转换器引发抽象类实例化失败问题求助
问题背景
为解决Stack<T>反序列化后元素顺序反转的问题,实现了自定义StackConverter并添加到JsonSerializerSettings中,但出现新错误:
Could not create an instance of type FTest.Total. Type is an interface or abstract class and cannot be instantiated. Path 'Basket.Totals[0].Value', line 17, position 16.
错误原因
你的JSON数据中包含$type字段(如"$type": "FTest.PointTargetAmount, FTest.Basket"),用于指定抽象类Total的具体实现类型,但当前JsonSerializerSettings中**TypeNameHandling被注释禁用**,导致在StackConverter的ReadJsonGeneric方法里调用serializer.Deserialize<List<T>>时,Json.NET无法识别$type信息,尝试直接实例化抽象类Total,从而抛出错误。
解决方案
方案1:启用TypeNameHandling
在JsonSerializerSettings中取消TypeNameHandling的注释,设置为Auto,让Json.NET自动识别并利用$type字段创建具体类型实例:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddJsonOptions(opt => { opt.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; // 取消注释并启用TypeNameHandling opt.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto; opt.SerializerSettings.DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset; opt.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat; opt.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.RoundtripKind; opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; opt.SerializerSettings.ConstructorHandling = Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor; opt.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; opt.SerializerSettings.Converters.Add(new StackConverter()); });
方案2:修改StackConverter以兼容类型信息(可选)
如果不想全局启用TypeNameHandling,可以修改ReadJsonGeneric方法,在反序列化列表时显式传递类型,确保Json.NET能处理$type字段:
object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; // 改用Deserialize的重载,指定类型,确保类型信息被处理 var list = serializer.Deserialize(reader, typeof(List<T>)) as List<T>; var stack = existingValue as Stack<T> ?? (Stack<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); for (int i = list.Count - 1; i >= 0; i--) stack.Push(list[i]); return stack; }
注意:此方案仍需确保
TypeNameHandling未被设置为None,否则依然无法识别$type字段。
补充说明
TypeNameHandling.Auto会在序列化多态类型时自动添加$type字段,反序列化时也会读取该字段创建正确的具体实例,正好匹配你的JSON数据格式。- 若担心类型注入安全问题,可以配合
SerializationBinder限制允许反序列化的类型范围。
内容的提问来源于stack exchange,提问作者songqx

