如何实现可通过静态Create方法按参数名称映射反序列化值对象的通用Newtonsoft.Json转换器
如何实现可通过静态Create方法按参数名称映射反序列化值对象的通用Newtonsoft.Json转换器
我太懂你这种头疼的感觉了——用私有构造+静态Create方法的方式实现值对象,确实能保证对象的完整性,但碰上Newtonsoft.Json反序列化就卡壳了,尤其是想做个通用转换器不用每个值对象都写专属逻辑的话。不过别慌,咱们可以实现一个通用的JsonConverter,它能自动找到值对象的静态Create方法,把JSON里的属性按名称映射到方法参数,最终调用Create方法实例化对象。
核心思路
核心逻辑就是靠反射完成几件关键事:
- 定位目标值对象类型里的公开静态
Create方法 - 解析JSON中的所有属性键值对
- 忽略大小写匹配JSON属性与
Create方法的参数(适配JSON驼峰命名和C#帕斯卡命名的差异) - 转换参数类型并调用
Create方法生成实例
通用转换器实现代码
public class ValueObjectConverter : JsonConverter<ValueObject> { public override bool CanConvert(Type objectType) { // 仅处理继承自ValueObject的非抽象类型 return typeof(ValueObject).IsAssignableFrom(objectType) && !objectType.IsAbstract; } public override void WriteJson(JsonWriter writer, ValueObject value, JsonSerializer serializer) { // 序列化直接用默认逻辑,值对象的公开属性都能正常输出 var jo = JObject.FromObject(value); jo.WriteTo(writer); } public override ValueObject ReadJson(JsonReader reader, Type objectType, ValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; // 将JSON读取为JObject,方便遍历属性 var jo = JObject.Load(reader); // 查找目标类型的静态Create方法(默认匹配唯一同名且返回当前类型的方法) var createMethod = objectType.GetMethods(BindingFlags.Public | BindingFlags.Static) .FirstOrDefault(m => m.Name == "Create" && m.ReturnType == objectType); if (createMethod == null) throw new JsonSerializationException($"类型 {objectType.FullName} 未找到符合要求的公开静态Create方法"); var parameters = createMethod.GetParameters(); var parameterValues = new object[parameters.Length]; foreach (var param in parameters) { // 忽略大小写匹配JSON属性 var property = jo.Properties() .FirstOrDefault(p => string.Equals(p.Name, param.Name, StringComparison.OrdinalIgnoreCase)); if (property == null) { // 参数有默认值则用默认值,否则抛出缺失必填参数的异常 if (param.HasDefaultValue) { parameterValues[param.Position] = param.DefaultValue; } else { throw new JsonSerializationException($"类型 {objectType.FullName} 的Create方法缺少必填参数 {param.Name}"); } } else { // 将JSON值转换为参数对应的类型 parameterValues[param.Position] = property.Value.ToObject(param.ParameterType, serializer); } } // 调用Create方法生成值对象实例 var instance = createMethod.Invoke(null, parameterValues); return (ValueObject)instance; } }
使用方式
你可以通过两种方式注册这个转换器:
- 全局注册:让所有继承自
ValueObject的类型自动使用该转换器,适合项目中统一配置:
var settings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new ValueObjectConverter() }, TypeNameHandling = TypeNameHandling.Auto // 对应你示例中的$type字段,确保反序列化时识别具体类型 }; // 反序列化示例 var json = @"{""$type"":""Entities.Individual, myassembly"",""name"":{""$type"":""ValueObjects.PersonName, myassembly"",""firstName"": ""John"",""middleName"": """",""lastName"": ""Wayne""},""citizenship"": ""American""}"; var individual = JsonConvert.DeserializeObject<Individual>(json, settings);
- 特性标记:仅给特定值对象使用该转换器,灵活性更高:
[JsonConverter(typeof(ValueObjectConverter))] public sealed class PersonName : ValueObject { // 你的值对象实现代码... }
注意事项
- 转换器默认假设每个值对象只有一个符合要求的
Create方法,如果存在多个重载,你需要在代码中添加更严格的筛选逻辑(比如根据参数数量、类型匹配)。 - 若
Create方法包含必填参数,JSON中必须提供对应属性;带默认值的可选参数可省略,转换器会自动使用参数默认值。 - 序列化无需额外处理,值对象的公开可读属性能被Newtonsoft.Json正常序列化。
- 使用
TypeNameHandling.Auto时,建议结合SerializationBinder限制允许反序列化的类型,避免安全风险。
备注:内容来源于stack exchange,提问作者user23137927
相关产品推荐
相关产品推荐

