如何在Newtonsoft.Json反序列化时适配API响应中对象/数组类型的Response字段
解决方案
你可以通过自定义JsonConverter实现单个对象/数组的自动兼容,不需要修改已有的实体类结构,步骤如下:
步骤1:实现通用的兼容转换器
新建一个泛型转换器类,自动判断JSON节点类型,单个对象时自动包装为长度为1的数组:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; public class SingleOrArrayConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(List<T>); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); // 节点为数组类型时直接反序列化为List if (token.Type == JTokenType.Array) { return token.ToObject<List<T>>(); } // 节点为单个对象时,包装为只有1个元素的List返回 return new List<T> { token.ToObject<T>() }; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } public override bool CanWrite => true; }
注意:如果API可能返回Response为null的情况,可以在
ReadJson方法开头加判空逻辑,避免空引用异常。
步骤2:给泛型响应类加特性标注
修改你定义的ApiGenericResponse<T>类,给Response字段指定使用上面的自定义转换器:
using Newtonsoft.Json; using System.Collections.Generic; namespace publicapi { public class ApiGenericResponse<T> { public string Get { get; set; } public Dictionary<string, string> Parameters { get; set; } public List<object> Errors { get; set; } public int Results { get; set; } public Paging Paging { get; set; } [JsonProperty("Response")] [JsonConverter(typeof(SingleOrArrayConverter<T>))] public List<T> Response { get; set; } } }
步骤3:统一反序列化调用逻辑
之后不管API返回的Response是单个对象还是数组,都用同一个泛型结构反序列化即可:
// 返回数组的接口用法不变 ApiGenericResponse<ApiFixResponse> fixResp = JsonConvert.DeserializeObject<ApiGenericResponse<ApiFixResponse>>(jsonContent); // 之前返回单个对象的接口,会自动转为长度为1的List,取第一个元素即可使用 ApiGenericResponse<ApiStatResponse> statResp = JsonConvert.DeserializeObject<ApiGenericResponse<ApiStatResponse>>(jsonContent); ApiStatResponse singleStat = statResp.Response[0];
原理解释
自定义JsonConverter会在反序列化对应字段时优先触发:
- 先读取JSON节点的类型,判断是数组还是单个对象
- 按对应逻辑处理后统一返回
List<T>类型,完全避免类型不匹配的序列化异常 - 已生成的所有实体类不需要做任何修改,直接复用即可
需要绑定ListBox的场景直接使用整个Response属性,需要绑定单个控件的场景取Response[0]即可,完全兼容现有业务逻辑。
内容的提问来源于stack exchange,提问作者Juan
相关产品推荐
相关产品推荐

