C#反序列化含混合类型字段的嵌套JSON报错解决方案
问题根因
反序列化报错的核心原因是接口返回的partitions字段类型不统一,不符合静态类型语言的反序列化默认规则:
- 部分返回记录里
partitions是空字符串:"partitions": "" - 部分记录里
partitions是标准的Partition对象数组:"partitions": [{"ref": "txtRef", "object1": "test", "dateFormat": "YYmmdd"}] - 部分记录里
partitions是空数组:"partitions": []
你定义的实体类中把partitions固定声明为List<Partition>类型,Json.NET解析到字符串值时无法直接将其转换为集合类型,就会抛出字符解析错误。Python属于动态类型语言,对同字段多类型的兼容度更高,所以相同JSON可以正常解析。
之前尝试的几种方案不生效的原因也很明确:
- 用dynamic接收只是绕过了编译时类型检查,后续访问
partitions做集合操作时,遇到字符串值依然会抛出运行时错误,没有解决根本问题 - 移除
partitions字段相当于直接跳过该字段解析,本质是规避问题不是解决问题 - 在线JSON转C#工具默认基于同名字段类型完全一致的规则生成代码,遇到这种接口返回不规范的多类型字段,生成的类自然无法正常反序列化。
解决方案
方案1:自定义JsonConverter兼容多类型(推荐,侵入性最低)
通过自定义JSON转换器,在反序列化阶段自动兼容partitions字段的多种格式,不需要修改后续业务逻辑:
- 编写自定义转换器,遇到空字符串、null值时直接返回空集合,遇到数组时正常反序列化为
List<Partition>:
public class PartitionListConverter : JsonConverter<List<Partition>> { public override List<Partition> ReadJson(JsonReader reader, Type objectType, List<Partition> existingValue, bool hasExistingValue, JsonSerializer serializer) { return reader.TokenType switch { JsonToken.String or JsonToken.Null => new List<Partition>(), JsonToken.StartArray => JArray.Load(reader).ToObject<List<Partition>>(serializer), _ => throw new JsonSerializationException($"不支持的partitions字段类型: {reader.TokenType}") }; } public override void WriteJson(JsonWriter writer, List<Partition> value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
- 给实体类的
partitions字段添加转换器标记:
public class dataSourceOptions { [JsonConverter(typeof(PartitionListConverter))] public List<Partition> partitions { get; set; } }
修改完成后,原有JsonConvert.DeserializeObject<testData>(response)代码不需要做任何调整,即可正常解析所有格式的返回值。
方案2:弱类型接收后二次转换
如果不想编写自定义转换器,可以先把partitions声明为通用JToken类型,反序列化完成后自行判断类型做转换:
- 修改
dataSourceOptions类定义:
public class dataSourceOptions { public JToken partitions { get; set; } }
- 反序列化后手动处理类型转换:
testData data = JsonConvert.DeserializeObject<testData>(response); foreach (var collectItem in data.dataList) { List<Partition> realPartitions = new List<Partition>(); if (collectItem.dataOptions.partitions is JArray arr) { realPartitions = arr.ToObject<List<Partition>>(); } // 后续业务逻辑直接使用realPartitions即可 }
内容的提问来源于stack exchange,提问作者Boonrak K.
相关产品推荐
相关产品推荐

