使用JsonConvert.DeserializeObject结合DynamicObject等处理无默认构造函数的EntityBase
没问题,我来给你一步步解决这个反序列化的问题!你的EntityBase继承了DynamicObject又没有无参构造函数,确实需要自定义转换器来告诉Json.NET怎么正确创建实例并处理动态属性。下面是具体的实现方案:
核心思路
要实现这个需求,我们需要自定义一个继承自TypeCreationConverter<EntityBase>的转换器,它的作用有两个:
- 调用
EntityBase的带参构造函数创建实例 - 把JSON中的动态属性映射到
EntityBase内部的values字典中
1. 编写自定义转换器EntityBaseConverter
这个转换器会负责实例创建和属性赋值,因为values是私有字段,我们需要用反射来访问它(如果你的类能提供公开的属性添加方法,优先用公开方法更符合封装原则):
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Converters; using System.Reflection; public class EntityBaseConverter : TypeCreationConverter<EntityBase> { // 重写Create方法,调用带参构造函数创建实例 protected override EntityBase Create(Type objectType, JObject jObject) { // 从JSON中提取构造函数需要的EntityName参数 var entityName = jObject["EntityName"]?.Value<string>(); if (string.IsNullOrWhiteSpace(entityName)) { throw new JsonSerializationException("JSON中必须包含EntityName字段,否则无法创建EntityBase实例"); } return new EntityBase(entityName); } // 重写ReadJson方法,处理动态属性赋值 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); // 先调用Create方法获取实例 var entity = Create(objectType, jObject); // 获取私有values字典的字段信息 var valuesField = typeof(EntityBase).GetField( "values", BindingFlags.NonPublic | BindingFlags.Instance); if (valuesField == null) { throw new JsonSerializationException("无法找到EntityBase的私有values字段"); } var valuesDict = (Dictionary<string, object>)valuesField.GetValue(entity); // 遍历JSON中的所有属性,除了EntityName,都存入values字典 foreach (var prop in jObject.Properties()) { if (prop.Name != nameof(EntityBase.EntityName)) { valuesDict[prop.Name] = prop.Value.ToObject(prop.Value.Type, serializer); } } return entity; } }
2. 配置Json.NET使用转换器
有两种方式可以让Json.NET使用这个转换器,选其中一种即可:
方式一:给EntityBase类添加特性标记
直接在你的EntityBase类上加上JsonConverter特性,这样每次序列化/反序列化都会自动使用这个转换器:
[JsonConverter(typeof(EntityBaseConverter))] public class EntityBase : DynamicObject { // 你的类原有代码... public string EntityName { get; private set; } private readonly Dictionary<string, object> values = new Dictionary<string, object>(); public EntityBase(string entityName) { this.EntityName = entityName; } public virtual object this[string fieldname] { get => values.TryGetValue(fieldname, out var val) ? val : null; set => values[fieldname] = value; } // 记得重写TryGetMember和TrySetMember,让动态属性访问生效 public override bool TryGetMember(GetMemberBinder binder, out object result) { return values.TryGetValue(binder.Name, out result); } public override bool TrySetMember(SetMemberBinder binder, object value) { values[binder.Name] = value; return true; } }
方式二:反序列化时手动传入转换器设置
如果不想修改类的特性,可以在反序列化时创建JsonSerializerSettings并添加转换器:
var json = @"{""EntityName"":""Customer"", ""Age"":30, ""Name"":""John""}"; var settings = new JsonSerializerSettings(); settings.Converters.Add(new EntityBaseConverter()); var entity = JsonConvert.DeserializeObject<EntityBase>(json, settings);
3. 测试验证
你可以用下面的代码测试反序列化效果,不管是通过索引器还是动态类型访问属性都能正常工作:
static void Main(string[] args) { var json = @"{""EntityName"":""Product"", ""Price"":99.99, ""Stock"":100}"; var entity = JsonConvert.DeserializeObject<EntityBase>(json); // 通过索引器访问 Console.WriteLine($"EntityName: {entity.EntityName}"); Console.WriteLine($"Price: {entity["Price"]}"); // 通过动态类型访问(因为继承了DynamicObject) dynamic dynEntity = entity; Console.WriteLine($"Stock: {dynEntity.Stock}"); }
注意事项
- 如果你的
EntityBase后续能提供公开的方法(比如AddField(string key, object value))来操作values字典,建议替换掉反射逻辑,这样代码更安全也更易维护。 - 确保JSON中必须包含
EntityName字段,否则转换器会抛出异常,你可以根据自己的需求调整异常处理逻辑(比如给个默认值)。
内容的提问来源于stack exchange,提问作者user1859022
相关产品推荐
相关产品推荐

