C#反序列化含$特殊键JSON时$type字段无法映射类属性问题
问题原因
- 特性使用错误:你代码中添加的
[JsonPropertyName("$type")]是System.Text.Json序列化库的专属特性,而你调用的JsonConvert.DeserializeObject属于*Newtonsoft.Json(Json.NET)*库,该特性不会被Json.NET识别,无法完成属性映射。 - 框架默认拦截
$type字段:Json.NET内置了多态类型反序列化机制,默认会将JSON中名为$type的键作为类型元数据解析,不会将其作为普通属性映射到实体类,即使标记了正确特性也会被框架优先拦截。 - 额外的结构不匹配问题:你的JSON中
CustomerIsMarried节点的Value是布尔类型true,IsSuspicious是布尔类型false,和你当前实体类定义的string Value、缺失IsSuspicious属性的结构不匹配,即使解决$type映射问题也会抛出转换错误。
修复方案
1. 修正实体类定义
- 引入Newtonsoft.Json命名空间,将所有
[JsonPropertyName("$type")]替换为Json.NET专属的[JsonProperty("$type")]特性 - 补全缺失的
IsSuspicious属性 - 将
Value属性类型改为object,兼容字符串、布尔值两种类型的赋值
修正后的实体类代码如下:
using Newtonsoft.Json; using System.Collections.Generic; public class DocumentDTO { public string Version { get; set; } public List<DocumentInfo> Documents { get; set; } } public class DocumentInfo { public string DocumentState { get; set; } public DocumentData DocumentData { get; set; } public string DocumentAsBase64 { get; set; } } public class DocumentData { public string Name { get; set; } [JsonProperty("$type")] public string type { get; set; } public List<DocumentField> Fields { get; set; } } public class DocumentField { public string Name { get; set; } [JsonProperty("$type")] public string type { get; set; } public string SuspiciousSymbols { get; set; } public string RecognizedValue { get; set; } public object Value { get; set; } public bool? IsSuspicious { get; set; } }
2. 修改反序列化配置
反序列化时传入自定义配置,将MetadataPropertyHandling设置为Ignore,告诉Json.NET不要将$type当做内置元数据处理,按普通字段映射即可:
string jsonText="{\"Version\":\"1.1\",\"Documents\":[{\"DocumentState\":\"Correct\",\"DocumentData\":{\"Name\":\"test\",\"$type\":\"Document\",\"Fields\":[{\"Name\":\"CustomerFullName\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"0\",\"RecognizedValue\":\"\",\"Value\":\"\"},{\"Name\":\"CustomerBirthDate\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"DocumentNumber\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"0000000000\",\"RecognizedValue\":\"\",\"Value\":\"\"},{\"Name\":\"CustomerIsMarried\",\"$type\":\"Checkmark\",\"IsSuspicious\":false,\"Value\":true},{\"Name\":\"CustomerCounty\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"CustomerArea\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"CustomerAddress\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"11000000\",\"RecognizedValue\":\"\",\"Value\":\"\"},{\"Name\":\"DocumentGUID\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"DocumentProposalID\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"DocumentCountry\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"000\",\"RecognizedValue\":\"FRA\",\"Value\":\"FRA\"},{\"Name\":\"DocumentCurrency\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"000\",\"RecognizedValue\":\"EUR\",\"Value\":\"EUR\"},{\"Name\":\"DocumentTo\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"DocumentFrom\",\"$type\":\"Text\",\"Value\":\"\"},{\"Name\":\"DocumentTotalNumberOfPages\",\"$type\":\"Text\",\"SuspiciousSymbols\":\"0\",\"RecognizedValue\":\"1\",\"Value\":\"1\"}]}}]}"; var serializerSettings = new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore }; var documentResult = JsonConvert.DeserializeObject<DocumentDTO>(jsonText, serializerSettings);
补充说明
如果你本身就是要使用Json.NET的多态反序列化能力,不需要手动读取$type的值,可以直接删掉实体类里的type属性,去掉上述自定义配置,Json.NET会自动根据$type的值反序列化为对应派生类实例。
内容的提问来源于stack exchange,提问作者core_user
相关产品推荐
相关产品推荐

