【问题标题】:json.net: specify converter for dictionary keysjson.net:为字典键指定转换器
【发布时间】:2011-07-27 13:43:21
【问题描述】:

我有一个 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; }

但 MyEnum 的值是 CodeACodeB 而不是分别简单的 AB

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

【问题讨论】:

    标签: c# json.net


    【解决方案1】:

    我相信唯一的方法是为整个Dictionary&lt;MyEnum, int&gt; 类型或Dictionary&lt;MyEnum, T&gt; 制作一个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);
        }
    

    希望对你有用。

    【讨论】:

    • 这在移动平台上使用 Json.Net 时特别有用,因为不支持 TypeConverters,例如需要这种方法。结构也可以作为键。
    • 如何正确使用您的转换器?我有类似的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&lt;InsightsMetric, long&gt; MetricValues { get; set; } } 但在我的示例中,转换器从未达到...
    【解决方案2】:

    这里是一个通用的解决方案,可以解决任何类型的键在字典中使用 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;
        }
    }
    

    【讨论】:

    • 如何使用这个通用解决方案?
    【解决方案3】:

    我无法让任何 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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-31
      • 1970-01-01
      • 1970-01-01
      • 2017-06-22
      • 2017-01-16
      • 2020-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多