内容提要
本文讨论了在使用System.Text.Json.JsonSerializer序列化字典对象时,字典的键不能是自定义类型(如Point)的解决方案。提出了通过自定义PointKeyedDictionaryConverter以键值对形式有效序列化字典的方法,避免了创建中间对象,从而提升了性能。
延伸解读
为何自定义JsonConverter对字典键无效
文章指出,为Point类型定义JsonConverter或TypeConverter都无法解决序列化Dictionary<Point, int>时抛出的NotSupportedException。异常并非发生在序列化Point对象时,而是在序列化字典时,系统会验证键类型是否合法,自定义类型键未通过验证。因此,转换器必须针对字典类型本身,而非键类型。
两种中间对象方案的性能权衡
文章提出了两种基于字典转换器的方案:一是将字典作为键值对集合序列化,二是转换为Dictionary<string, TValue>再序列化。后者生成的JSON更精炼,但两者都需要创建中间对象(键值对集合或字符串键字典),在性能要求高的场景下并非最优。
直接读写JSON避免中间对象
最终方案通过自定义JsonConverter直接控制JSON读写:Write方法用Utf8JsonWriter逐对写入属性,Read方法用Utf8JsonReader循环读取键值并构造字典。这样避免了创建中间集合或字典,生成的JSON结构与前两种方案一致,但性能更优。
Q&A
为什么自定义类型不能作为字典的键进行序列化?
自定义类型在使用System.Text.Json.JsonSerializer序列化字典时会抛出NotSupportedException异常,因为默认情况下字典的键必须是可序列化的类型。
如何解决自定义类型作为字典键无法序列化的问题?
可以通过自定义PointKeyedDictionaryConverter<TValue>以键值对形式序列化字典,避免创建中间对象,从而提升性能。
使用Newtonsoft.Json序列化自定义类型字典有什么不同?
使用Newtonsoft.Json可以成功序列化自定义类型字典,但反序列化时会失败,而System.Text.Json则在序列化时直接抛出异常。
自定义JsonConverter能否解决字典键为自定义类型的问题?
自定义JsonConverter无法解决字典键为自定义类型的问题,因为异常在序列化字典时就已抛出,而不是在处理Point对象时。
如何将以Point为键的字典转换为以字符串为键的字典?
可以通过重写PointKeyedDictionaryConverter<TValue>中的Write和Read方法,将Dictionary<Point, TValue>转换为Dictionary<string, TValue>进行序列化和反序列化。
自定义PointKeyedDictionaryConverter<TValue>的性能优势是什么?
该方法避免了创建中间对象,从而提升了性能,直接以键值对形式进行序列化和反序列化。