您可以使用JsonConverterFactory https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverterfactory制造特定的JsonConverter<T> https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter-1对于要序列化为数组的每个字典类型。这是一个这样的转换器,适用于实现的每个类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 https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.converters?System_Text_Json_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 中全局使用,如下所示如何在asp.net core 3中设置json序列化器设置? https://stackoverflow.com/q/58392039/3744182:
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new DictionaryConverterFactory());
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
底层的单独转换器DictionaryAsArrayConverter<TKey, TValue>
如果只想将某些字典类型序列化为数组,也可以直接使用。
Notes:
JsonSerializer
目前不尊重PropertyNamingPolicy https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.propertynamingpolicy序列化时KeyValuePair<TKey, TValue>
(see 问题 #1197 https://github.com/dotnet/runtime/issues/1197)所以我不得不介绍一个KeyValueDTO
得到外壳"key"
and "value"
按照你的问题的要求。
我没有实现非通用转换器IDictionary
类型。这可以作为答案的延伸来完成。
有关转换器工厂模式的更多信息,请参阅如何在 .NET 中编写用于 JSON 序列化的自定义转换器:示例工厂模式转换器 https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#sample-factory-pattern-converter
类型相当于DefaultContractResolver
in System.Text.Json
-- JsonClassInfo https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs and JsonPropertyInfo https://github.com/dotnet/corefx/blob/master/src/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfo.cs -- are internal。有一个开放的增强功能System.Text.Json 中的 DefaultContractResolver 的等效项 #42001 https://github.com/dotnet/corefx/issues/42001要求公共同等的。
演示小提琴here https://dotnetfiddle.net/0ApBYW.