如何将无法序列化的自定义Key字典转换为可序列化形式?

摘要:当我们使用System.Text.Json.JsonSerializer对一个字典对象进行序列化的时候,默认情况下字典的Key不能是一个自定义的类型,本文介绍几种解决方案。一、问题重现 二、自定义JsonConverter能解决吗? 三、自
当我们使用System.Text.Json.JsonSerializer对一个字典对象进行序列化的时候,默认情况下字典的Key不能是一个自定义的类型,本文介绍几种解决方案。 一、问题重现 二、自定义JsonConverter能解决吗? 三、自定义TypeConverter能解决问题吗? 四、以键值对集合的形式序列化 五、转换成合法的字典 六、自定义读写 一、问题重现我们先通过如下这个简单的例子来重现上述这个问题。如代码片段所示,我们定义了一个名为Point(代表二维坐标点)的只读结构体作为待序列化字典的Key。Point可以通过结构化的表达式来表示,我们同时还定义了Parse方法将表达式转换成Point对象。 using System.Diagnostics; using System.Text.Json; var dictionary = new Dictionary<Point, int> { { new Point(1.0, 1.0), 1 }, { new Point(2.0, 2.0), 2 }, { new Point(3.0, 3.0), 3 } }; try { var json = JsonSerializer.Serialize(dictionary); Console.WriteLine(json); var dictionary2 = JsonSerializer.Deserialize<Dictionary<Point, int>>(json)!; Debug.Assert(dictionary2[new Point(1.0, 1.0)] == 1); Debug.Assert(dictionary2[new Point(2.0, 2.0)] == 2); Debug.Assert(dictionary2[new Point(3.0, 3.0)] == 3); } catch (Exception ex) { Console.WriteLine(ex.Message); } public readonly record struct Point(double X, double Y) { public override string ToString()=> $"({X}, {Y})"; public static Point Parse(string s) { var tokens = s.Trim('(',')').Split(',', StringSplitOptions.TrimEntries); if (tokens.Length != 2) { throw new FormatException("Invalid format"); } return new Point(double.Parse(tokens[0]), double.Parse(tokens[1])); } }当我们使用JsonSerializer序列化多一个Dictionary<Point, int>类型的对象时,会抛出一个NotSupportedException异常,如下所示的信息解释了错误的根源:Point类型不能作为被序列化字典对象的Key。顺便说一下,如果使用Newtonsoft.Json,这样的字典可以序列化成功,但是反序列化会失败。 二、自定义JsonConverter<Point>能解决吗?遇到这样的问题我们首先想到的是:既然不执行针对Point的序列化/反序列化,那么我们可以对应相应的JsonConverter自行完成序列化/反序列化工作。为此我们定义了如下这个PointConverter,将Point的表达式作为序列化输出结果,同时调用Parse方法生成反序列化的结果。
阅读全文