System.Text.Json 将字典序列化为数组



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

而不是{ "hello": "world" }我需要将我的字典序列化为{ "key": "hello", "value": "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);
}
}

您可以使用JsonConverterFactory来执行此操作,该为要序列化为数组的每个字典类型制造特定的JsonConverter<T>。 这是一个这样的转换器,适用于实现IDictionary<TKey, TValue>的每个类:

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 中全局显示,如如何在 Core 3 中设置 json 序列化程序设置 asp.net 所示?

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

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

笔记:

  • JsonSerializer目前在序列化KeyValuePair<TKey, TValue>时不尊重PropertyNamingPolicy(请参阅问题 #1197(,因此我不得不引入一个KeyValueDTO来按照您的问题的要求"key""value"大小写。

  • 我没有为非泛型IDictionary类型实现转换器。 这可以作为答案的延伸。

  • 有关转换器工厂模式的详细信息,请参阅如何在 .NET 中为 JSON 序列化编写自定义转换器:示例工厂模式转换器

  • 等效于System.Text.Json中的DefaultContractResolver的类型(JsonClassInfoJsonPropertyInfo(是内部类型System.Text.Json #42001 中有一个开放的增强等效于 DefaultContractResolver,要求公共等效项。

演示小提琴在这里。

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

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?"}]

最新更新