如何反序列化以枚举元组为键的Dictionary属性?
解决Dictionary<Tuple<Color, Shape>, Stuff>的JSON反序列化问题
核心问题是Json.NET默认将Tuple<Color, Shape>序列化为类似"(Red, Round)"的字符串,反序列化时无法自动将该字符串解析回Tuple类型。解决办法是自定义Json转换器,控制Tuple键的序列化格式和反序列化逻辑,无需修改第三方类或原有业务模型。
1. 自定义Tuple转换器
创建继承自JsonConverter<Tuple<Color, Shape>>的转换器,实现自定义的序列化和反序列化逻辑:
using Newtonsoft.Json; using System; using System.Collections.Generic; public class TupleColorShapeConverter : JsonConverter<Tuple<Color, Shape>> { // 使用竖线作为枚举值的分隔符,可根据需求调整 private const string Separator = "|"; public override void WriteJson(JsonWriter writer, Tuple<Color, Shape> value, JsonSerializer serializer) { // 将Tuple序列化为"Color|Shape"格式的字符串,避免默认的带括号格式 var keyString = $"{value.Item1}{Separator}{value.Item2}"; writer.WriteValue(keyString); } public override Tuple<Color, Shape> ReadJson(JsonReader reader, Type objectType, Tuple<Color, Shape> existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.String) throw new JsonSerializationException("Tuple键必须是字符串格式"); var keyString = reader.Value.ToString(); var parts = keyString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new JsonSerializationException($"无效的键格式:{keyString},请使用'Color|Shape'格式"); // 支持忽略大小写解析枚举,提升鲁棒性 if (!Enum.TryParse(parts[0], true, out Color color)) throw new JsonSerializationException($"无效的Color枚举值:{parts[0]}"); if (!Enum.TryParse(parts[1], true, out Shape shape)) throw new JsonSerializationException($"无效的Shape枚举值:{parts[1]}"); return Tuple.Create(color, shape); } }
2. 使用转换器进行序列化/反序列化
方式一:显式指定序列化配置
在序列化和反序列化时,将转换器传入JsonSerializerSettings:
// 初始化测试数据 var shelvingUnit = new ShelvingUnit(); shelvingUnit.Bins.Add(Tuple.Create(Color.Red, Shape.Round), new Stuff { Purpose = "Storage", NumberOfThings = 10 }); shelvingUnit.Classifiers.Add(Tuple.Create(Color.Blue, Shape.Rectangular)); // 配置序列化设置 var settings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new TupleColorShapeConverter() }, Formatting = Formatting.Indented // 可选,格式化JSON便于阅读 }; // 序列化 var json = JsonConvert.SerializeObject(shelvingUnit, settings); // 反序列化 var deserializedUnit = JsonConvert.DeserializeObject<ShelvingUnit>(json, settings);
方式二:全局默认配置
如果业务代码中多处需要处理该类型,可以全局注册转换器,避免重复配置:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = new List<JsonConverter> { new TupleColorShapeConverter() } }; // 后续直接使用JsonConvert.SerializeObject/DeserializeObject即可 var json = JsonConvert.SerializeObject(shelvingUnit); var deserializedUnit = JsonConvert.DeserializeObject<ShelvingUnit>(json);
3. 为什么之前的尝试未成功?
- 修改JSON键格式:手动修改仅能临时解决单次反序列化问题,序列化时会重新生成默认格式,无法持久解决;
- TypeConverter:Json.NET默认不会自动使用
TypeConverter处理字典键的转换,需结合自定义转换器才能生效; - 拆分列表重建字典:复杂业务场景下易遗漏数据,且代码冗余,无法应对嵌套或动态数据结构。
内容的提问来源于stack exchange,提问作者Eric Eggers
相关产品推荐
相关产品推荐

