Newtonsoft Json无法反序列化继承类型(含@odata.type元数据)
问题描述
我已经搜索过相关问题,但没找到可行方案。我有一个包含类型元数据"@odata.type": "#SomeType"的JSON文件,其中SomeType继承自SomeTypeBase,但反序列化后仅得到基类对象。我尝试设置TypeNameHandling.All但无效,是不是@odata.type导致的问题?注:这是一个.NET 7项目。
更新
相关代码如下:
public static T Deserialize<T>(string serialized) { try { using var reader = new StringReader(serialized); using var reader2 = new JsonTextReader(reader) { DateParseHandling = DateParseHandling.DateTimeOffset, FloatParseHandling = FloatParseHandling.Decimal, }; return Serializer.Deserialize<T>(reader2); } catch (Exception ex) { throw new JsonSerializationException(ex.Message, ex); } }
Serializer为静态实例:
public static readonly JsonSerializer Serializer = new() { TypeNameHandling = TypeNameHandling.All, NullValueHandling = NullValueHandling.Ignore, DateParseHandling = DateParseHandling.DateTimeOffset, FloatParseHandling = FloatParseHandling.Decimal, };
解决方案
问题确实出在@odata.type字段上——Newtonsoft.Json默认只会识别$type字段来完成多态反序列化,不会处理@odata.type。要解决这个问题,需要自定义IContractResolver替换默认的类型识别逻辑,让它读取@odata.type的值并映射到对应的实际类型。
步骤1:实现自定义ContractResolver
创建继承自DefaultContractResolver的类,重写相关方法修改类型识别字段,并处理@odata.type中的#前缀:
public class ODataTypeNameContractResolver : DefaultContractResolver { protected override JsonArrayContract CreateArrayContract(Type objectType) { var contract = base.CreateArrayContract(objectType); contract.ItemTypeNameHandling = TypeNameHandling.Auto; contract.ItemTypeNamePropertyName = "@odata.type"; return contract; } protected override JsonObjectContract CreateObjectContract(Type objectType) { var contract = base.CreateObjectContract(objectType); contract.TypeNameHandling = TypeNameHandling.Auto; contract.TypeNamePropertyName = "@odata.type"; return contract; } public override Type ResolveType(string typeName) { if (string.IsNullOrEmpty(typeName)) return base.ResolveType(typeName); // 移除@odata.type值中的#前缀 var cleanedTypeName = typeName.TrimStart('#'); // 替换为你的类型命名空间和程序集名称 var fullTypeName = $"YourNamespace.{cleanedTypeName}, YourAssemblyName"; return Type.GetType(fullTypeName) ?? base.ResolveType(typeName); } }
注意:需将YourNamespace和YourAssemblyName替换为实际的类型命名空间与程序集名称,若类型在当前程序集内,程序集名称可省略。
步骤2:配置Serializer使用自定义ContractResolver
修改静态Serializer的初始化代码,添加自定义解析器:
public static readonly JsonSerializer Serializer = new() { TypeNameHandling = TypeNameHandling.All, NullValueHandling = NullValueHandling.Ignore, DateParseHandling = DateParseHandling.DateTimeOffset, FloatParseHandling = FloatParseHandling.Decimal, ContractResolver = new ODataTypeNameContractResolver() // 添加此行 };
步骤3:验证效果
假设基类与子类定义如下:
namespace YourNamespace { public class SomeTypeBase { public string BaseProperty { get; set; } } public class SomeType : SomeTypeBase { public string DerivedProperty { get; set; } } }
对应的JSON内容:
{ "@odata.type": "#SomeType", "BaseProperty": "基础属性", "DerivedProperty": "派生属性" }
调用Deserialize<SomeTypeBase>(json)后,得到的对象会是SomeType实例,可正常访问DerivedProperty。
补充说明
- 若类型分布在不同程序集,需在
ResolveType方法中正确拼接完整类型名称(包含程序集信息),确保Type.GetType能定位到目标类型。 - 若
@odata.type的值已包含命名空间(如#YourNamespace.SomeType),可直接使用cleanedTypeName作为完整类型名,无需额外拼接。
内容的提问来源于stack exchange,提问作者milandjukic88
相关产品推荐
相关产品推荐

