Json.NET Dictionary <string,T> with StringComparer序列化

Bug*_*Bug 18 c# json.net

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

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

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

Bri*_*ers 17

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

class CaseInsensitiveDictionary<V> : Dictionary<string, V>
{
    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<CaseInsensitiveDictionary<CaseInsensitiveDictionary<object>>>(json);

        Console.WriteLine(dict["foo"]["fizz"]);
        Console.WriteLine(dict["foo"]["buzz"]);
        Console.WriteLine(dict["bar"]["dit"]);
        Console.WriteLine(dict["bar"]["dah"]);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

1
yo
3.14
True
Run Code Online (Sandbox Code Playgroud)


Jef*_*ado 9

最好创建一个可以根据需要创建字典对象的转换器.这正是Newtonsoft.Json.Converters.CustomCreationConverter<T>为此而设计的.

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

public class CustomComparerDictionaryCreationConverter<T> : CustomCreationConverter<IDictionary>
{
    private IEqualityComparer<T> comparer;
    public CustomComparerDictionaryCreationConverter(IEqualityComparer<T> 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<T>) }) != null;
    }

    public override IDictionary Create(Type objectType)
    {
        return Activator.CreateInstance(objectType, comparer) as IDictionary;
    }
}
Run Code Online (Sandbox Code Playgroud)

但请注意,此转换器将应用于密钥协变的所有字典类型T,而不管值类型如何.

然后使用它:

var converters = new JsonConverter[]
{
    new CustomComparerDictionaryCreationConverter<string>(StringComparer.OrdinalIgnoreCase),
};
var dict = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, object>>>(jsonString, converters);
Run Code Online (Sandbox Code Playgroud)