我在为个人项目生成一些JSON时也遇到了这个问题——我有一个递归的多态数据模型,想要将其转换为JSON,但是根对象中派生类型的子对象存在时,序列化程序只会输出基类型的属性作为所有派生类型的属性。我花了几个小时使用JsonConverter和反射来摆弄,并想出了一个能够实现我的需求的铁锤式解决方案。
基本上,这是在手动遍历图中的每个对象,并对不是引用类型的每个成员使用默认的序列化程序进行序列化,但是当遇到引用类型(不是字符串)的实例时,我会动态生成一个新的JsonConverter来处理该类型并将其添加到“转换器”列表中,然后递归地使用该新实例将子对象序列化为其真正的运行时类型。
你可能可以将其用作解决方案的起点,以实现你所需的功能。
转换器:
public class RuntimeTypeJsonConverter<T> : JsonConverter<T>
{
private static readonly Dictionary<Type, PropertyInfo[]> _knownProps = new Dictionary<Type, PropertyInfo[]>();
private static readonly Dictionary<Type, JsonConverter> _knownConverters = new Dictionary<Type, JsonConverter>();
private static readonly Dictionary<Type, Type> _knownGenerics = new Dictionary<Type, Type>();
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.IsClass && typeToConvert != typeof(string);
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var deserialized = JsonSerializer.Deserialize(ref reader, typeToConvert, options);
return (T)deserialized;
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value is IEnumerable)
{
WriteIEnumerable(writer, value, options);
}
else if (value != null && value.GetType().IsClass == true)
{
WriteObject(writer, value, ref options);
}
else
{
JsonSerializer.Serialize(writer, value);
}
}
private void WriteObject(Utf8JsonWriter writer, T value, ref JsonSerializerOptions options)
{
var type = value.GetType();
PropertyInfo[] props = GetPropertyInfos(type);
writer.WriteStartObject();
foreach (var prop in props)
{
var propVal = prop.GetValue(value);
if (propVal == null) continue;
writer.WritePropertyName(prop.Name);
var propType = propVal.GetType();
if (propType.IsClass && propType != typeof(string))
{
Type generic = GetGenericConverterType(propType);
JsonConverter converter = GetJsonConverter(generic);
var found = false;
foreach (var converterInUse in options.Converters)
{
if (converterInUse.GetType() == generic)
{
found = true;
break;
}
}
if (found == false)
{
options = new JsonSerializerOptions(options);
options.Converters.Add(converter);
}
JsonSerializer.Serialize(writer, propVal, propType, options);
}
else
{
JsonSerializer.Serialize(writer, propVal);
}
}
writer.WriteEndObject();
}
private Type GetGenericConverterType(Type propType)
{
Type generic = null;
if (_knownGenerics.ContainsKey(propType) == false)
{
generic = typeof(RuntimeTypeJsonConverter<>).MakeGenericType(propType);
_knownGenerics.Add(propType, generic);
}
else
{
generic = _knownGenerics[propType];
}
return generic;
}
private JsonConverter GetJsonConverter(Type genericType)
{
JsonConverter converter = null;
if (_knownConverters.ContainsKey(genericType) == false)
{
converter = (JsonConverter)Activator.CreateInstance(genericType);
_knownConverters.Add(genericType, converter);
}
else
{
converter = _knownConverters[genericType];
}
return converter;
}
private PropertyInfo[] GetPropertyInfos(Type t)
{
PropertyInfo[] props = null;
if (_knownProps.ContainsKey(t) == false)
{
props = t.GetProperties();
_knownProps.Add(t, props);
}
else
{
props = _knownProps[t];
}
return props;
}
private void WriteIEnumerable(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
writer.WriteStartArray();
foreach (object item in value as IEnumerable)
{
if (item == null)
{
writer.WriteNullValue();
continue;
}
JsonSerializer.Serialize(writer, item, item.GetType(), options);
}
writer.WriteEndArray();
}
}
使用方法:
var cars = new List<Car>
{
new Tesla(),
new Tesla(),
new Tesla()
};
var options = new JsonSerializerOptions();
options.Converters.Add(new RuntimeTypeJsonConverter<object>());
var json = JsonSerializer.Serialize(cars, cars.GetType(), options);
Tesla
类型,否则只会获取Car
属性。 - Martin CostelloList<Tesla>
作为一个选项。 - Pavel AnikhouskiList<object>
。或者...继续使用Json.net。 - Lasse V. Karlsen