如何让Json.NET序列化字典时保留键但跳过默认值内容?
我最近碰到这么个需求:用Json.NET序列化字典的时候,希望跳过那些和默认值一致的字段值,但得保留对应的键,这样后续反序列化的时候还能完整恢复结构。举个具体的场景:
我有这么一段代码:
public Dictionary<int, Item> allItems; public class Item { public bool IsSelected; public List<string> SelectionInfo; }
现在用工具序列化后得到的JSON是这样的:
"allItems": { "0": { "IsSelected": true, "SelectionInfo": [ "yes", "maybe", "no" ] }, "1": { "IsSelected": false, "SelectionInfo": [] } }
但我想要的输出是这样的——把默认值的字段去掉,但保留键:
"allItems": { "0": { "IsSelected": true, "SelectionInfo": [ "yes", "maybe", "no" ] }, "1": { } }
而且序列化工具在另一个项目里,调用方式是Utility.Please.TurnToJson(item);,我还得保证跨项目的JSON序列化一致性。如果必须用自定义转换器,得知道怎么把它传给这个工具,比如类似Utility.Please.TurnToJson(item, new List<object>(){ typeof(Custom1), typeof(Custom2) });这种调用方式。
核心解决方案:自定义JsonConverter
要实现这个需求,核心就是写一个针对Item类的自定义JsonConverter,在序列化时判断属性是否为默认值,只保留非默认值的字段;反序列化时补全默认值,确保结构完整。
1. 编写自定义转换器
先写一个ItemConverter,继承JsonConverter<Item>:
public class ItemConverter : JsonConverter<Item> { public override void WriteJson(JsonWriter writer, Item value, JsonSerializer serializer) { var obj = new JObject(); // 判断IsSelected是否不是默认值(bool默认是false) if (value.IsSelected != default(bool)) { obj.Add(nameof(Item.IsSelected), value.IsSelected); } // 判断SelectionInfo是否有内容(这里默认是空列表,所以判断Count>0) if (value.SelectionInfo?.Count > 0) { obj.Add(nameof(Item.SelectionInfo), JArray.FromObject(value.SelectionInfo, serializer)); } obj.WriteTo(writer); } public override Item ReadJson(JsonReader reader, Type objectType, Item existingValue, bool hasExistingValue, JsonSerializer serializer) { // 反序列化时补全默认值,保证结构完整 var obj = JObject.Load(reader); var item = new Item(); item.IsSelected = obj.TryGetValue(nameof(Item.IsSelected), out var isSelectedToken) ? isSelectedToken.ToObject<bool>(serializer) : default(bool); item.SelectionInfo = obj.TryGetValue(nameof(Item.SelectionInfo), out var selectionInfoToken) ? selectionInfoToken.ToObject<List<string>>(serializer) : new List<string>(); return item; } }
2. 传入自定义转换器到工具
根据你提到的工具调用方式,它接受一个包含转换器类型的List<object>参数,那调用的时候直接传ItemConverter的类型即可:
var customConverters = new List<object> { typeof(ItemConverter) }; string serializedJson = Utility.Please.TurnToJson(yourObjectInstance, customConverters);
这里要注意:工具内部需要把传入的类型实例化成JsonConverter对象,然后添加到JsonSerializerSettings的Converters集合里,所以你的ItemConverter必须有无参构造函数(上面的例子已经满足)。
3. 替代方案:用特性标记类
如果工具暂时不支持传入转换器参数,你可以直接在Item类上标记JsonConverter特性,这样Json.NET会自动使用这个转换器,跨项目只要引用了这个类和转换器,就能保证序列化一致:
[JsonConverter(typeof(ItemConverter))] public class Item { public bool IsSelected; public List<string> SelectionInfo; }
注意事项
- 默认值的判断要根据实际业务来调整:比如
SelectionInfo如果默认是null,那判断逻辑就要改成value.SelectionInfo != null,而不是Count>0。 - 反序列化一定要补全默认值:这样后续恢复对象时,不会出现属性为
null或者不符合预期默认值的情况,保证结构完整性。
内容的提问来源于stack exchange,提问作者ToonWK

