Json.NET Dictionary with StringComparer序列化

我有一个字典Dictionary<string, Dictionary> 。 外部字典和内部字典都有一个相等比较器集(在我的例子中是StringComparer.OrdinalIgnoreCase )。 字典序列化和反序列化后,两个字典的比较器都未设置为StringComparer.OrdinalIgnoreCase

如果您可以控制代码中字典的创建,则可以创建从字典inheritance的类,并在类的默认构造函数中设置comparer。 但是,如果您无法控制字典创建并从其他代码获取字典,该怎么办?

有没有办法用比较器正确序列化/反序列化?

一个简单的想法是创建Dictionary的子类,默认情况下将比较器设置为StringComparer.OrdinalIgnoreCase ,然后反序列化为而不是普通字典。 例如:

 class CaseInsensitiveDictionary : Dictionary { public CaseInsensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase) { } } class Program { static void Main(string[] args) { string json = @" { ""Foo"" : { ""fiZZ"" : 1, ""BUzz"" : ""yo"" }, ""BAR"" : { ""dIt"" : 3.14, ""DaH"" : true } }"; var dict = JsonConvert.DeserializeObject>>(json); Console.WriteLine(dict["foo"]["fizz"]); Console.WriteLine(dict["foo"]["buzz"]); Console.WriteLine(dict["bar"]["dit"]); Console.WriteLine(dict["bar"]["dah"]); } } 

输出:

 1 yo 3.14 True 

最好创建一个可以根据需要创建字典对象的转换器。 这正是Newtonsoft.Json.Converters.CustomCreationConverter设计目的。

这是一个可以创建需要自定义比较器的字典的实现。

 public class CustomComparerDictionaryCreationConverter : CustomCreationConverter { private IEqualityComparer comparer; public CustomComparerDictionaryCreationConverter(IEqualityComparer comparer) { if (comparer == null) throw new ArgumentNullException("comparer"); this.comparer = comparer; } public override bool CanConvert(Type objectType) { return HasCompatibleInterface(objectType) && HasCompatibleConstructor(objectType); } private static bool HasCompatibleInterface(Type objectType) { return objectType.GetInterfaces() .Where(i => HasGenericTypeDefinition(i, typeof(IDictionary<,>))) .Where(i => typeof(T).IsAssignableFrom(i.GetGenericArguments().First())) .Any(); } private static bool HasGenericTypeDefinition(Type objectType, Type typeDefinition) { return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeDefinition; } private static bool HasCompatibleConstructor(Type objectType) { return objectType.GetConstructor(new Type[] { typeof(IEqualityComparer) }) != null; } public override IDictionary Create(Type objectType) { return Activator.CreateInstance(objectType, comparer) as IDictionary; } } 

但请注意,无论值类型如何,此转换器都将应用于键与T协变的所有字典类型。

然后使用它:

 var converters = new JsonConverter[] { new CustomComparerDictionaryCreationConverter(StringComparer.OrdinalIgnoreCase), }; var dict = JsonConvert.DeserializeObject>>(jsonString, converters); 

创建一个扩展方法,该方法将从区分大小写的字典中的值复制到新的不区分大小写的字典。

 public static Dictionary ToCaseInsensitive(this Dictionary caseSensitiveDictionary) { var caseInsensitiveDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); caseSensitiveDictionary.Keys.ToList() .ForEach(k => caseInsensitiveDictionary[k] = caseSensitiveDictionary[k]); return caseInsensitiveDictionary; } 

用法:

 var newDictionary = JsonConvert.DeserializeObject>(value) .ToCaseInsensitive(); 

虽然这对我有用(并且由于其简单性我喜欢这个解决方案),但请注意以下警告:

  • 复制时会产生轻微的开销
  • 如果字典中有重复的键(例如“cat”和“CAT”),则会覆盖一个键。 您可以轻松地调整方法以在这种情况下抛出exception(如果您愿意)。
  • 我的解决方案在反序列化期间并不严格使用比较器,但最有可能是使字典进入不区分大小写状态的最简单方法。