如何动态反序列化API返回JSON的可变Body至对应C#模型?
问题描述
我从API获取的JSON格式如下:
{ "header":{ "message_type":"message_type", "notification_type":"notification_type" }, "body":{ "id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "related_entity_type":"inbound_funds" } }
问题在于body可以包含任意数量和类型的属性,而且我已经有对应每种body类型的C#模型。有没有高效的方法能动态解析并把这些对象反序列化为对应的C#模型?
我尝试了以下代码,但Body无法在运行时完成反序列化:
public class PushNotification : Body { [JsonProperty("header")] public Header Header { get; set; } [JsonProperty("body")] public Body Body { get; set; } } public class Body { }
解决方案
可以利用**Json.NET(Newtonsoft.Json)**的特性,先解析头部信息确定Body类型,再针对性反序列化Body内容,这是最常用且高效的方案。
步骤1:修正基础模型
把PushNotification里的Body改成JToken类型,先暂存原始JSON内容,避免提前丢失结构:
public class PushNotification { [JsonProperty("header")] public Header Header { get; set; } [JsonProperty("body")] public JToken BodyRaw { get; set; } // 暂存原始Body的JSON结构 } public class Header { [JsonProperty("message_type")] public string MessageType { get; set; } [JsonProperty("notification_type")] public string NotificationType { get; set; } }
步骤2:建立类型映射字典
提前把API返回的标识(比如notification_type或Body里的related_entity_type)和对应的C#模型做绑定,方便快速查找:
// 全局或局部定义类型映射 private static readonly Dictionary<string, Type> BodyTypeMap = new Dictionary<string, Type> { {"inbound_funds", typeof(InboundFundsBody)}, {"outbound_payouts", typeof(OutboundPayoutsBody)}, // 后续新增Body类型直接在这里添加 }; // 示例Body模型 public class InboundFundsBody { [JsonProperty("id")] public Guid Id { get; set; } [JsonProperty("related_entity_type")] public string RelatedEntityType { get; set; } // 该类型专属的其他属性 } public class OutboundPayoutsBody { [JsonProperty("id")] public Guid Id { get; set; } [JsonProperty("amount")] public decimal Amount { get; set; } // 该类型专属的其他属性 }
步骤3:动态反序列化Body
先反序列化整个通知拿到头部信息,再根据映射字典找到对应模型,最后把原始Body内容转成目标类型:
string apiResponse = "API返回的JSON字符串"; // 1. 先反序列化整体,拿到头部和原始Body var pushNotify = JsonConvert.DeserializeObject<PushNotification>(apiResponse); // 2. 确定要使用的Body类型(这里用header里的notification_type,也可以从BodyRaw取related_entity_type) string typeKey = pushNotify.Header.NotificationType; // 或者:string typeKey = pushNotify.BodyRaw["related_entity_type"].ToString(); if (BodyTypeMap.TryGetValue(typeKey, out Type targetType)) { // 3. 把原始Body反序列化为目标模型 object body = JsonConvert.DeserializeObject(pushNotify.BodyRaw.ToString(), targetType); // 强转后使用 if (body is InboundFundsBody inboundBody) { // 处理入金业务逻辑 Console.WriteLine($"入金记录ID:{inboundBody.Id}"); } else if (body is OutboundPayoutsBody outboundBody) { // 处理出金业务逻辑 Console.WriteLine($"出金金额:{outboundBody.Amount}"); } } else { // 处理未知类型的异常 throw new NotSupportedException($"未定义的Body类型:{typeKey}"); }
进阶优化:自定义JsonConverter
如果想一次性完成反序列化,不用分步处理,可以自定义转换器,让反序列化过程自动识别Body类型:
public class PushNotificationConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(TypedPushNotification); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); // 先解析Header var header = jo["header"].ToObject<Header>(serializer); // 获取Body类型并验证 string typeKey = header.NotificationType; if (!BodyTypeMap.TryGetValue(typeKey, out Type bodyType)) { throw new NotSupportedException($"不支持的通知类型:{typeKey}"); } // 解析Body object body = jo["body"].ToObject(bodyType, serializer); return new TypedPushNotification { Header = header, Body = body }; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); // 不需要序列化可留空 } } // 定义带动态Body的通知类 public class TypedPushNotification { public Header Header { get; set; } public object Body { get; set; } }
使用时直接指定转换器即可:
var typedNotify = JsonConvert.DeserializeObject<TypedPushNotification>(apiResponse, new PushNotificationConverter());
注意事项
- 类型映射的键要和API返回的标识完全匹配(大小写敏感)
- 如果用.NET Core内置的
System.Text.Json,思路类似,用JsonElement代替JToken即可 - 务必处理未知Body类型的情况,避免程序崩溃
内容的提问来源于stack exchange,提问作者Tayyab
相关产品推荐
相关产品推荐

