Json.net:为字典键指定转换器

19

我有一个JSON:

{ 
    "data": { "A": 5, "B": 6 }, 
    "foo": "foo", 
    "bar": "bar" 
}

我需要将数据反序列化为一个类:

public Dictionary<MyEnum, int> Data { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }

但是我的枚举值是CodeACodeB,而不是简单的AB

我有一个自定义的转换器可以处理转换。但是我该如何指定一个JsonConverter与字典键一起使用?

3个回答

17
我认为唯一的方法是为整个 Dictionary<MyEnum, int> 类型或 Dictionary<MyEnum, T> 创建一个JsonConverter。 字典键不被视为值,并且不会通过JsonConverters运行。 TypeConverters可能是一种解决方案,但默认的字符串到枚举转换将在查看TypeConverters之前进行。 所以...我认为没有其他办法。
编辑:
并未完全测试,但我在我的一个项目中使用类似以下内容:
public class DictionaryWithSpecialEnumKeyConverter : JsonConverter
{
    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        var valueType = objectType.GetGenericArguments()[1];
        var intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(string), valueType);
        var intermediateDictionary = (IDictionary)Activator.CreateInstance(intermediateDictionaryType);
        serializer.Populate(reader, intermediateDictionary);

        var finalDictionary = (IDictionary)Activator.CreateInstance(objectType);
        foreach (DictionaryEntry pair in intermediateDictionary)
            finalDictionary.Add(Enum.Parse(MyEnum, "Code" + pair.Key, false), pair.Value);

        return finalDictionary;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsA(typeof(IDictionary<,>)) &&
               objectType.GetGenericArguments()[0].IsA<MyEnum>();
    }
}

你需要这个小助手:

    public static bool IsA(this Type type, Type typeToBe)
    {
        if (!typeToBe.IsGenericTypeDefinition)
            return typeToBe.IsAssignableFrom(type);

        var toCheckTypes = new List<Type> { type };
        if (typeToBe.IsInterface)
            toCheckTypes.AddRange(type.GetInterfaces());

        var basedOn = type;
        while (basedOn.BaseType != null)
        {
            toCheckTypes.Add(basedOn.BaseType);
            basedOn = basedOn.BaseType;
        }

        return toCheckTypes.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeToBe);
    }
希望你能成功。

1
这对于在移动平台上使用Json.Net尤其有帮助,因为TypeConverters不受支持,而且对于将结构体作为键的情况,需要采用这种方法。 - Justin Caldicott
如何正确使用您的转换器?我有类似的JSON。我想将其反序列化为下一个类:`public class InsightsReportDataMetric { [JsonProperty("programs")] public InsightsReportDataPrograms[] Programs { get; set; } = new InsightsReportDataPrograms[0]; [JsonProperty("date")] public string Date { get; set; } [JsonConverter(typeof(InsightsMetricConverter))] public Dictionary MetricValues { get; set; } }`但在我的示例中,转换器从未被触发... - demo

0

这里提供了一个通用的解决方案,用于解决使用带有任何类型键的字典与json的问题:

[JsonObject]
public class MyKeyValuePair<TKey, TValue>
{
    public TKey MyKey;

    public TValue MyValue;

    [JsonConstructor]
    public MyKeyValuePair()
    {

    }

    public MyKeyValuePair(TKey t1, TValue t2)
    {
        MyKey = t1;
        MyValue = t2;
    }
}



[JsonObject]
public class MyDictionaty<TKey, TValue>

{
    public ICollection<MyKeyValuePair<TKey, TValue>> Collection;

    [JsonConstructor]
    public MyDictionaty()
    {

    }
    public MyDictionaty(Dictionary<TKey, TValue> refund)
    {
        Collection = BuildMyKeyValuePairCollection(refund);
    }
    internal Dictionary<TKey, TValue> ToDictionary()
    {
        return Collection.ToDictionary(pair => pair.MyKey, pair => pair.MyValue);
    }

    private ICollection<MyKeyValuePair<TKey, TValue>> BuildMyKeyValuePairCollection(Dictionary<TKey, TValue> refund)
    {
        return refund.Select(o => new MyKeyValuePair<TKey, TValue>(o.Key, o.Value)).ToList();
    }
}




[JsonObject]
public class ClassWithDictionary
{
    [JsonProperty]
    private readonly MyDictionary<AnyKey, AnyValue> _myDictionary;

    private Dictionary<AnyKey, AnyValue> _dic;

    [JsonConstructor]
    public ClassWithDictionary()
    {

    }
    public ClassWithDictionary(Dictionary<AnyKey, AnyValue> dic)
    {
        _dic= dic;
        _myDictionary = new MyDictionaty<AnyKey, AnyValue>(dic);
    }

    public Dictionary<AnyKey, AnyValue> GetTheDictionary()
    {
        _dic = _dic??_myDictionary.ToDictionary();
        return _dic;
    }
}

1
如何使用这个通用解决方案? - sky91

0

我无法让任何TypeConverter解决方案起作用,也不想使用JsonConverter构建一个字符串键字典,然后将所有内容复制到新字典中,所以我选择了类似于这样的方法:

public sealed class MyEnumKeyDictionary<TValue> : IReadOnlyDictionary<MyEnum, TValue>, IDictionary<string, TValue>
{
    private readonly Dictionary<MyEnum, TValue> actual = new Dictionary<MyEnum, TValue>();

    // implement IReadOnlyDictionary implicitly, passing everything from `actual`

    // implement IDictionary explicitly, passing everything into/from `actual` after doing Enum.Parse/Enum.ToString
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接