如何将JSON反序列化为带非字符串(如FileInfo)键的Dictionary
问题描述
我编写了一段测试代码,尝试实现Dictionary<FileInfo, Point>的序列化与反序列化往返操作,但始终无法成功:
using System.Drawing; using Newtonsoft.Json; namespace JSON_Trials; class StackExample { public static void Main() { Dictionary<FileInfo, Point> dict_write = new() { {new("a_file_name.txt"), new(1, 2)}, }; var json_write = JsonConvert.SerializeObject(dict_write); File.WriteAllText("_dict.json", json_write); var json_read = File.ReadAllText("_dict.json"); var dict_read = JsonConvert.DeserializeObject<Dictionary<FileInfo, Point>>(json_read); Console.WriteLine(dict_read.Count); } }
运行时抛出如下错误:
Newtonsoft.Json.JsonSerializationException: 'Could not convert string 'a_file_name.txt' to dictionary key type 'System.IO.FileInfo'. Create a TypeConverter to convert from the string to the key type object. Path '['a_file_name.txt']', line 1, position 19.'
生成的JSON内容为:
{"a_file_name.txt":"1, 2"}
我尝试编写了多个TypeConverter、JsonConverter、ContractResolver实现,但均未生效。FileInfo本身带有单字符串构造函数,JSON格式也是标准的字典结构,理论上应该不难实现,到底遗漏了什么?是否存在可行方案?
场景限制
- 字典的Key是带单字符串构造函数的对象类型(例如
FileInfo(string filename)) - Key类型不可修改(无法修改源码、添加注解)
- JSON格式必须保持不变(不可改为数组形式)
- Value类型不影响实现逻辑
环境:.NET 7 & C# 11
解决方案
最终采用的代码如下(感谢@Serg),这里选用FullName作为序列化的键值,因为它更接近唯一标识符:
using System.ComponentModel; using System.Drawing; using System.Globalization; using Newtonsoft.Json; namespace JSON_Trials; class StackExample { public static void Main() { Dictionary<FileInfo, Point> dict_write = new() { {new("a_file_name.txt"), new(1, 2)}, }; TypeDescriptor.AddAttributes(typeof(FileInfo), new TypeConverterAttribute(typeof(FileInfoTypeConverter))); var json_write = JsonConvert.SerializeObject(dict_write); File.WriteAllText("_dict.json", json_write); var json_read = File.ReadAllText("_dict.json"); var dict_read = JsonConvert.DeserializeObject<Dictionary<FileInfo, Point>>(json_read); Console.WriteLine(dict_read.Count); } } internal class FileInfoTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is string str) return new FileInfo(str); return base.ConvertFrom(context, culture, value); } public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (value is FileInfo fi) return fi.FullName; return base.ConvertTo(context, culture, value, destinationType); } }
内容的提问来源于stack exchange,提问作者Xan-Kun Clark-Davis
相关产品推荐
相关产品推荐

