【问题标题】:Serialize Dictionary<,> as array in Json.NET [duplicate]在 Json.NET 中将 Dictionary<,> 序列化为数组 [重复]
【发布时间】:2017-06-07 07:25:00
【问题描述】:

如何让 Json.NET 序列化程序将 IDictionary&lt;,&gt; 实例序列化为具有键/值属性的对象数组? 默认情况下,它将 Key 的值序列化为 JSON 对象的属性名称。

基本上我需要这样的东西:

[{"key":"some key","value":1},{"key":"another key","value":5}]

代替:

{{"some key":1},{"another key":5}}

我尝试将KeyValuePairConverter 添加到序列化程序设置中,但没有效果。 (我发现IDictionary&lt;&gt; 的类型忽略了此转换器,但我无法轻易更改对象的类型,因为它们是从其他库接收的,因此从IDictionary&lt;&gt; 更改为ICollection&lt;KeyValuePair&lt;&gt;&gt; 对我来说不是选项。)

【问题讨论】:

    标签: json.net


    【解决方案1】:

    我能够让这个转换器工作。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;
    
    public class CustomDictionaryConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (typeof(IDictionary).IsAssignableFrom(objectType) || 
                    TypeImplementsGenericInterface(objectType, typeof(IDictionary<,>)));
        }
    
        private static bool TypeImplementsGenericInterface(Type concreteType, Type interfaceType)
        {
            return concreteType.GetInterfaces()
                   .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            Type type = value.GetType();
            IEnumerable keys = (IEnumerable)type.GetProperty("Keys").GetValue(value, null);
            IEnumerable values = (IEnumerable)type.GetProperty("Values").GetValue(value, null);
            IEnumerator valueEnumerator = values.GetEnumerator();
    
            writer.WriteStartArray();
            foreach (object key in keys)
            {
                valueEnumerator.MoveNext();
    
                writer.WriteStartObject();
                writer.WritePropertyName("key");
                writer.WriteValue(key);
                writer.WritePropertyName("value");
                serializer.Serialize(writer, valueEnumerator.Current);
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    以下是使用转换器的示例:

    IDictionary<string, int> dict = new Dictionary<string, int>();
    dict.Add("some key", 1);
    dict.Add("another key", 5);
    
    string json = JsonConvert.SerializeObject(dict, new CustomDictionaryConverter());
    Console.WriteLine(json);
    

    这是上面的输出:

    [{"key":"some key","value":1},{"key":"another key","value":5}]
    

    【讨论】:

    • 检查非泛型System.Collections.IDictionary 会更容易(也更完整)。泛型接口通常扩展非泛型接口。此外,您的TypeImplementsGenericInterface() 方法可以使用Any() 而不是Where().FirstOrDefault() != null 来简化。
    • 虽然很好,但IDictionary&lt;K,V&gt; 实际上并没有扩展非通用的IDictionary 接口。请参阅documentation。但是,为了完整起见,我编辑了我的答案以添加对IDictionary 的支持,并结合了您关于简化TypeImplementsGenericInterface() 方法的其他建议。谢谢!
    • 对了,我忘了。更正,immutable泛型接口一般都扩展了对应的immutable非泛型接口,可惜这里不适用。
    • 其他人觉得这很有用。如果要正确序列化 Dictionary 或类似内容,则需要替换 writer.WriteValue(valueEnumerator.Current);使用 serializer.Serialize(writer, valueEnumerator.Current) 以便正确序列化子对象
    • @ScottG 感谢您的反馈。您的建议甚至适用于像intstring 这样的简单类型,所以这绝对是要走的路。我已经相应地编辑了我的答案。谢谢!
    【解决方案2】:

    想出另一种方法 - 您可以创建自定义 ContractResolver 并在(反)序列化之前将其设置为 JsonSerializerSettings。下面的一个派生自内置的CamelCasePropertyNamesContractResolver,用于将序列化的属性名称转换为驼峰式,但如果您不想修改名称,它可以派生自DefaultContractResolver

    public class DictionaryFriendlyContractResolver : CamelCasePropertyNamesContractResolver
    {
        protected override JsonContract CreateContract(Type objectType)
        {
            if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                return new JsonArrayContract(objectType);
            if (objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
                return new JsonArrayContract(objectType);
            return base.CreateContract(objectType);
        }
    }
    

    用法:

    var cfg = new JsonSerializerSettings();
    cfg.ContractResolver = new DictionaryFriendlyContractResolver();
    string json = JsonConvert.SerializeObject(myModel, cfg);
    

    【讨论】:

    猜你喜欢
    • 2017-04-07
    • 1970-01-01
    • 2016-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-20
    • 2017-03-08
    相关资源
    最近更新 更多