如何反序列化时而为字典时而为空数组的JSON?
解决Newtonsoft.Json中字典/空数组混合结构的反序列化问题
问题场景
需要反序列化的JSON中,details字段存在两种结构:
- 正常数据时为字典结构(如示例中的
goodJson) - 无数据时为空数组(如示例中的
badJson)
直接反序列化会抛出JsonSerializationException,自定义转换器后虽不再报错,但正常结构的Details属性反序列化后为空字典——转换器内部能读取到数据,最终对象里却计数为0。
问题原因分析
之前的转换器大概率存在以下问题:
- 处理字典结构时,没有正确将JSON对象的键值对映射到
Dictionary<TKey, TValue>实例中 - 可能误将字典结构当成数组处理,或是创建字典实例后未正确填充数据
正确的自定义转换器实现
以下是兼容空数组和字典结构的转换器(以Dictionary<string, object>为例,可根据实际需求修改泛型参数):
public class DictionaryOrEmptyArrayConverter : JsonConverter<Dictionary<string, object>> { public override Dictionary<string, object> ReadJson(JsonReader reader, Type objectType, Dictionary<string, object> existingValue, bool hasExistingValue, JsonSerializer serializer) { // 处理空数组场景 if (reader.TokenType == JsonToken.StartArray) { serializer.Deserialize(reader, typeof(List<object>)); return new Dictionary<string, object>(); } // 处理字典/对象场景 if (reader.TokenType == JsonToken.StartObject) { var dict = existingValue ?? new Dictionary<string, object>(); var jObject = JObject.Load(reader); foreach (var property in jObject.Properties()) { dict[property.Name] = property.Value.ToObject<object>(serializer); } return dict; } // 其他异常场景返回空字典 return new Dictionary<string, object>(); } public override void WriteJson(JsonWriter writer, Dictionary<string, object> value, JsonSerializer serializer) { // 序列化时:字典为空则输出空数组,否则输出对象 if (value == null || value.Count == 0) { writer.WriteStartArray(); writer.WriteEndArray(); } else { serializer.Serialize(writer, value); } } }
使用方式
在目标类的Details属性上标记转换器:
public class MyModel { [JsonConverter(typeof(DictionaryOrEmptyArrayConverter))] public Dictionary<string, object> Details { get; set; } }
测试验证
针对两种JSON结构测试:
// 正常字典结构JSON var goodJson = @"{""Details"": {""key1"": ""value1"", ""key2"": ""value2"", ""key3"": ""value3"", ""key4"": ""value4""}}"; var goodModel = JsonConvert.DeserializeObject<MyModel>(goodJson); Console.WriteLine(goodModel.Details.Count); // 输出4 // 空数组结构JSON var badJson = @"{""Details"": []}"; var badModel = JsonConvert.DeserializeObject<MyModel>(badJson); Console.WriteLine(badModel.Details.Count); // 输出0,无报错
关键修复点
- 明确区分
JsonToken.StartArray和JsonToken.StartObject两种类型,避免逻辑混淆 - 处理字典时,通过
JObject.Load(reader)加载完整对象数据,遍历属性填充字典,确保所有键值对被正确添加 - 复用已有字典实例(
existingValue),避免因创建新实例导致数据丢失
内容的提问来源于stack exchange,提问作者sbridewell
相关产品推荐
相关产品推荐

