您的问题在于,转换器在此处递归调用了自身:
return (TBase)JsonSerializer.Deserialize(root.GetRawText(), type, options);
递归发生的原因是,在 CanConvert(Type typeToConvert)
方法中,即使 ClassC
的具体派生类型无需转换,也会返回 true
。
由于您的基类是抽象的,您可以将转换器修改为在 CanConvert
方法内部添加一个检查,确保传入的类型是抽象的:
public class ClassCJsonConverter : JsonConverter<ClassC>
{
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsAssignableFrom(typeof(ClassC)) && typeToConvert.IsAbstract;
public override ClassC? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
{
var root = jsonDoc.RootElement;
var typeDiscriminator = root.GetProperty("TypeDiscriminator").GetString();
var type = typeDiscriminator == null ? null : Type.GetType(typeDiscriminator);
if (type == null || !CanConvert(type)) // 在这里我们保证不会无限递归。
throw new JsonException($"无法找到或无效类型: {typeDiscriminator}");
return (ClassC?)root.Deserialize(type, options);
}
}
public override void Write(Utf8JsonWriter writer, ClassC value, JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, value, value.GetType(), options);
}
这个版本假设任何可分配给 ClassC
的类型要么是抽象的(需要进行类型解析),要么是具体的(不需要类型解析)。如果存在非抽象基类型,则此策略将不起作用,您需要采用一种更复杂的方法,如在递归调用期间禁用转换器,就像在 How to use default serialization in a custom System.Text.Json JsonConverter? 中所示那样。
示例代码 #1 可在 此处 查看。
另外,从 .NET 7 开始,System.Text.Json 内置了对多态类型区分符的支持,因此不再需要自定义转换器。请参阅关于 Is polymorphic deserialization possible in System.Text.Json? 的这个答案。使用这种内置方法,只需向您的类应用以下属性即可实现序列化和反序列化:
[JsonDerivedType(typeof(ClassB), nameof(ClassB))]
[JsonDerivedType(typeof(ClassA), nameof(ClassA))]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "TypeDiscriminator")]
public abstract class ClassC
{
public string PropertyC { get; set; }
}
[JsonDerivedType(typeof(ClassB), nameof(ClassB))]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "TypeDiscriminator")]
public class ClassB : ClassC
{
public string PropertyB { get; set; }
}
[JsonDerivedType(typeof(ClassA), nameof(ClassA))]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "TypeDiscriminator")]
public class ClassA : ClassB
{
public string PropertyA { get; set; }
}
可能需要根据 C# 命名空间调整传递给 [JsonDerivedType(Type derivedType, string typeDiscriminator)]
属性构造函数中的类型区分符值,因为 <code>nameof(type)</code> 并不包含 C# 命名空间。
示例代码 #2 可在 此处 查看。