I have this rather simple test and trying to get it working for days now, without success:
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);
}
}
Basically, I'm just trying to round trip the Dictionary.
In this basic form, it creates the following error:
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.'
Just for completeness, this is the generated json in the file:
{"a_file_name.txt":"1, 2"}
I have written many TypeConverters, JsonConverters, ContractResolverthingys and such and none of these approaches work. What am I missing here? Is there a way to do this at all? It should be super easy, right? I mean FileInfo has a single string constructor and the json is in the format of a Dictionary anyways. Any hint is much appreciated even if the "solution" might not be straight forward.
Since there are 3 (three!) valid solutions now, some clarification of this particular scenario seems appropriate:
- The
Key
of the Dictionary is a type of object, that has a 'single string' constructor (in this case,FileInfo(string filename)
). - The type of the
Key
is not controlled (source code can not be changed, no annotations added). - The JSON should stay the same (no arrays).
- The
Value
doesn't really matter here.
P.S.: For all versions, we can just assume the most recent one. atm: .NET 7 & C# 11
Solution
This is the final code I ended up with, thanks @Serg! I also use FullName, as the full name is close enough to a unique identifier for me :-)
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);
}
}