ASP.NET Core中如何无类型字段反序列化JSON到派生类?
问题描述
前端生成的RuleGroup JSON结构如下(包含嵌套的RuleGroup和RuleCriteria节点):
{ "combinator": "OR", "not": true, "criteria": [ { "field": "path", "key": "", "value": "", "operator": "=" }, { "combinator": "AND", "not": false, "criteria": [ { "field": "header", "key": "", "value": "", "operator": "=" } ] } ] }
对应的C#类型定义如下,但反序列化时Criteria列表中的元素均被序列化为BaseRule基类对象,无法转换为对应的派生类:
public class RuleGroup : BaseRule { public string Combinator { get; set; } public bool Not { get; set; } public List<BaseRule> Criteria { get; set; } } public class RuleCriteria : BaseRule { public string Field { get; set; } public string Operator { get; set; } public string Key { get; set; } public string Value { get; set; } } public class BaseRule { }
不想为JSON数据添加专用类型鉴别字段,也不想手动编写完整的反序列化器,询问是否有其他可行的实现方式。
解决方案
无需手动编写完整反序列化逻辑,可通过基于属性存在性的自定义类型转换器实现多态反序列化,以下是两种主流JSON库的实现方案:
1. 使用Newtonsoft.Json(Json.NET)
自定义转换器,通过判断JSON对象中是否包含combinator或field属性来区分派生类:
public class BaseRuleConverter : JsonConverter<BaseRule> { public override BaseRule ReadJson(JsonReader reader, Type objectType, BaseRule existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); // 包含combinator属性则实例化RuleGroup if (jObject.ContainsKey("combinator")) { var ruleGroup = new RuleGroup(); serializer.Populate(jObject.CreateReader(), ruleGroup); return ruleGroup; } // 包含field属性则实例化RuleCriteria else if (jObject.ContainsKey("field")) { var criteria = new RuleCriteria(); serializer.Populate(jObject.CreateReader(), criteria); return criteria; } throw new JsonSerializationException("无法识别的BaseRule类型"); } public override void WriteJson(JsonWriter writer, BaseRule value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
使用方式二选一:
- 在
BaseRule类上标记转换器:[JsonConverter(typeof(BaseRuleConverter))] public class BaseRule { } - 反序列化时指定转换器:
var ruleGroup = JsonConvert.DeserializeObject<RuleGroup>(jsonString, new BaseRuleConverter());
2. 使用System.Text.Json
自定义转换器,通过JsonElement判断属性存在性:
public class BaseRuleConverter : JsonConverter<BaseRule> { public override BaseRule Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using (var doc = JsonDocument.ParseValue(ref reader)) { var root = doc.RootElement; if (root.TryGetProperty("combinator", out _)) { return JsonSerializer.Deserialize<RuleGroup>(root.GetRawText(), options); } else if (root.TryGetProperty("field", out _)) { return JsonSerializer.Deserialize<RuleCriteria>(root.GetRawText(), options); } throw new JsonException("无法识别的BaseRule类型"); } } public override void Write(Utf8JsonWriter writer, BaseRule value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
使用方式二选一:
- 在序列化配置中添加转换器:
var options = new JsonSerializerOptions(); options.Converters.Add(new BaseRuleConverter()); var ruleGroup = JsonSerializer.Deserialize<RuleGroup>(jsonString, options); - 在
BaseRule类上标记转换器:[JsonConverter(typeof(BaseRuleConverter))] public class BaseRule { }
以上方案会自动根据JSON中的属性特征判断实例化对应派生类,无需额外类型字段,也不用编写完整的反序列化逻辑。
内容的提问来源于stack exchange,提问作者Darc
相关产品推荐
相关产品推荐

