如何基于JSON的method字段实现C#泛型JSON转换器?
不用写几百个case!优雅实现基于method字段的泛型JSON转换
你完全没必要靠庞大的switch语句来处理几百种模型,这不仅维护起来像噩梦,还违反了开闭原则(新增模型还要修改转换逻辑)。下面给你两种实用的解决方案,尤其适合大量模型的场景:
方案一:类型映射字典 + 反射(最推荐)
核心思路是提前建立method字符串到对应模型类型的映射,然后通过反射动态调用反序列化方法,完全不用写switch。
步骤1:统一模型的基类/接口(可选但推荐)
先给所有模型定义一个共同的接口,方便后续统一处理:
public interface IRequestModel { } public class Model : IRequestModel { public string Key { get; set; } } public class OtherModel : IRequestModel { public string Foo { get; set; } }
步骤2:定义泛型容器和结果类
和你的原型保持一致,加上泛型约束:
public class JsonData<T> where T : IRequestModel { public string Method { get; set; } // 对应JSON中的method字段 public T Payload { get; set; } } public class LogInfoRequestResult<T> : IResult where T : IRequestModel { public JsonData<T> Raw { get; set; } // 实现IResult的其他成员... }
步骤3:实现动态转换逻辑
这里关键是用类型字典替代switch,并且用JObject解析JSON(比Regex可靠100倍,不会因为JSON格式变化出错):
public class JsonPayloadConverter { // 静态字典:method名称 → 模型类型 private static readonly Dictionary<string, Type> _modelTypeMap; static JsonPayloadConverter() { _modelTypeMap = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); // 【手动注册】适合少量模型,或者用下面的【自动注册】适合几百个模型 _modelTypeMap.Add("Model", typeof(Model)); _modelTypeMap.Add("OtherModel", typeof(OtherModel)); // 【自动注册】扫描程序集中所有实现IRequestModel的类型,无需手动添加 // var assembly = Assembly.GetExecutingAssembly(); // var modelTypes = assembly.GetTypes() // .Where(t => typeof(IRequestModel).IsAssignableFrom(t) // && !t.IsInterface // && !t.IsAbstract); // foreach (var type in modelTypes) // { // // 默认用类名作为method值,也可以自定义Attribute指定别名 // _modelTypeMap.Add(type.Name, type); // } } public IResult ParseJson(string json) { // 先解析JSON,安全提取method字段 var jObject = JObject.Parse(json); if (!jObject.TryGetValue("method", out var methodToken) || methodToken.Type != JTokenType.String) { throw new InvalidOperationException("JSON格式错误:缺少或无效的'method'字段"); } var methodName = methodToken.Value<string>(); if (!_modelTypeMap.TryGetValue(methodName, out var modelType)) { throw new NotSupportedException($"不支持的method类型:{methodName}"); } // 动态创建泛型的JsonData<T>并反序列化 var jsonDataGenericType = typeof(JsonData<>).MakeGenericType(modelType); var rawData = JsonConvert.DeserializeObject(json, jsonDataGenericType); // 动态创建泛型的LogInfoRequestResult<T>并赋值 var resultGenericType = typeof(LogInfoRequestResult<>).MakeGenericType(modelType); var result = Activator.CreateInstance(resultGenericType); result.GetType().GetProperty(nameof(LogInfoRequestResult<IRequestModel>.Raw))? .SetValue(result, rawData); return (IResult)result; } }
方案二:利用Newtonsoft.Json的自定义转换器(进阶)
如果想让序列化过程更透明,可以自定义一个JsonConverter,直接根据method字段反序列化为对应的泛型类型:
public class RequestResultConverter : JsonConverter<IResult> { private static readonly Dictionary<string, Type> _modelTypeMap = new(StringComparer.OrdinalIgnoreCase) { { "Model", typeof(LogInfoRequestResult<Model>) }, { "OtherModel", typeof(LogInfoRequestResult<OtherModel>) } // 同样可以自动注册 }; public override IResult ReadJson(JsonReader reader, Type objectType, IResult existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); if (!jObject.TryGetValue("method", out var methodToken)) { throw new JsonSerializationException("缺少method字段"); } var methodName = methodToken.Value<string>(); if (!_modelTypeMap.TryGetValue(methodName, out var resultType)) { throw new JsonSerializationException($"不支持的method:{methodName}"); } return (IResult)jObject.ToObject(resultType, serializer); } public override void WriteJson(JsonWriter writer, IResult value, JsonSerializer serializer) { // 如果需要序列化IResult,在这里实现逻辑 throw new NotImplementedException(); } }
使用的时候直接反序列化为IResult即可:
var result = JsonConvert.DeserializeObject<IResult>(json, new RequestResultConverter());
为什么不用switch?
- 维护成本:几百个模型的switch会非常冗长,新增/删除模型都要修改这个方法,容易出错
- 开闭原则:上述方案遵循开闭原则,新增模型只需要添加类(或标记Attribute),无需修改转换逻辑
- 可靠性:用
JObject解析JSON比Regex更稳定,能处理各种合法的JSON格式(比如转义字符、字段顺序变化等)
内容的提问来源于stack exchange,提问作者Bruno
相关产品推荐
相关产品推荐

