【问题标题】:System.Text.Json Serialize dictionary as arraySystem.Text.Json 将字典序列化为数组
【发布时间】:2020-01-08 10:36:36
【问题描述】:

是否可以使用 System.Text.Json 将字典序列化(和反序列化)为数组?

我需要将我的字典序列化为{ "key": "hello", "value": "world" } 而不是{ "hello": "world" },最好不必在我的类的字典属性上设置属性。

使用 newtonsoft.json 可以这样:

class DictionaryAsArrayResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type objectType)
    {
        if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) || 
           (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
        {
            return base.CreateArrayContract(objectType);
        }

        return base.CreateContract(objectType);
    }
}

【问题讨论】:

    标签: c# .net-core system.text.json


    【解决方案1】:

    您可以使用JsonConverterFactory 来执行此操作,该JsonConverter&lt;T&gt; 为您想要序列化为数组的每个字典类型生成一个特定的JsonConverter&lt;T&gt;。这是一个适用于每个实现IDictionary&lt;TKey, TValue&gt; 的类的转换器:

    public class DictionaryConverterFactory : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            return typeToConvert.IsClass && typeToConvert.GetDictionaryKeyValueType() != null && typeToConvert.GetConstructor(Type.EmptyTypes) != null;
        }
    
        public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
        {
            var keyValueTypes = typeToConvert.GetDictionaryKeyValueType();
            var converterType = typeof(DictionaryAsArrayConverter<,,>).MakeGenericType(typeToConvert, keyValueTypes.Value.Key, keyValueTypes.Value.Value);
            return (JsonConverter)Activator.CreateInstance(converterType);
        }
    }
    
    public class DictionaryAsArrayConverter<TKey, TValue> : DictionaryAsArrayConverter<Dictionary<TKey, TValue>, TKey, TValue>
    {
    }
    
    public class DictionaryAsArrayConverter<TDictionary, TKey, TValue> : JsonConverter<TDictionary> where TDictionary : class, IDictionary<TKey, TValue>, new()
    {
        struct KeyValueDTO
        {
            public TKey Key { get; set; }
            public TValue Value { get; set; }
        }
    
        public override TDictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var list = JsonSerializer.Deserialize<List<KeyValueDTO>>(ref reader, options);
            if (list == null)
                return null;
            var dictionary = typeToConvert == typeof(Dictionary<TKey, TValue>) ? (TDictionary)(object)new Dictionary<TKey, TValue>(list.Count) : new TDictionary();
            foreach (var pair in list)
                dictionary.Add(pair.Key, pair.Value);
            return dictionary;
        }
    
        public override void Write(Utf8JsonWriter writer, TDictionary value, JsonSerializerOptions options)
        {
            JsonSerializer.Serialize(writer, value.Select(p => new KeyValueDTO { Key = p.Key, Value = p.Value }), options);
        }
    }
    
    public static class TypeExtensions
    {
        public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
        {
            if (type == null)
                throw new ArgumentNullException();
            if (type.IsInterface)
                return new[] { type }.Concat(type.GetInterfaces());
            else
                return type.GetInterfaces();
        }
    
        public static KeyValuePair<Type, Type>? GetDictionaryKeyValueType(this Type type)
        {
            KeyValuePair<Type, Type>? types = null;
            foreach (var pair in type.GetDictionaryKeyValueTypes())
            {
                if (types == null)
                    types = pair;
                else
                    return null;
            }
            return types;
        }
    
        public static IEnumerable<KeyValuePair<Type, Type>> GetDictionaryKeyValueTypes(this Type type)
        {
            foreach (Type intType in type.GetInterfacesAndSelf())
            {
                if (intType.IsGenericType
                    && intType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                {
                    var args = intType.GetGenericArguments();
                    if (args.Length == 2)
                        yield return new KeyValuePair<Type, Type>(args[0], args[1]);
                }
            }
        }
    }
    

    然后在本地添加工厂到JsonSerializerOptions.Converters,如下:

    var options = new JsonSerializerOptions
    {
        Converters = { new DictionaryConverterFactory() },
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    };
    
    var json = JsonSerializer.Serialize(dictionary, options);
    
    var dictionary2 = JsonSerializer.Deserialize<TDictionary>(json, options);
    

    或全局在 ASP.NET Core 中如 How to set json serializer settings in asp.net core 3? 所示:

    services.AddControllers().AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new DictionaryConverterFactory());
        options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    });
    

    如果您只想将某些字典类型序列化为数组,也可以直接使用底层的单个转换器DictionaryAsArrayConverter&lt;TKey, TValue&gt;

    注意事项:

    演示小提琴here.

    【讨论】:

    • 我认为调用Dictionary.ToList() 会更快更简单,但我喜欢你展示的深度自定义
    • @JoshE - 当字典是根对象时返回一个列表是有意义的,但是 OP 的问题指出它是 我的类的属性,并暗示 OP 没有想修改模型。在这种情况下,转换器工厂是合适的。此外,如注释中所述,JsonSerializer 目前不尊重PropertyNamingPolicyKeyValuePair&lt;TKey, TValue&gt;,因此暗示返回ToList() 将导致不正确的大小写。
    • 在这两点上都足够公平,我从 OP 问他们问题的方式推断得太多
    【解决方案2】:

    如果您想保持简短,可以考虑通过匿名类型进行投影:

    var dictionary = new Dictionary<string, string>();
    dictionary.Add("hello", "world");
    dictionary.Add("how", "are you?");
    
    var o = JsonSerializer.Serialize(dictionary.Select(x => new { key = x.Key, value = x.Value }));
    // [{"key":"hello","value":"world"},{"key":"how","value":"are you?"}]
    

    ed:当然,这只是你的自虐感。如果您只想完成工作,请致电.ToList()

    JsonSerializer.Serialize(dictionary.ToList());
    // [{"Key":"hello","Value":"world"},{"Key":"how","Value":"are you?"}]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多