我不明白为什么下面的代码在Write期间抛出StackOverflowException.我希望能够使用类型鉴别器来帮助我序列化/反序列化对象,同时不丢失具体的实现细节.我如何解决此问题?

using System.Text.Json;
using System.Text.Json.Serialization;

public abstract class ClassC
{
    public string PropertyC { get; set; }
    public string TypeDiscriminator => this.GetType().FullName;
}

public class ClassB : ClassC
{
    public string PropertyB { get; set; }
}

public class ClassA : ClassB
{
    public string PropertyA { get; set; }
}

public class PolymorphicJsonConverter<TBase> : JsonConverter<TBase> where TBase : ClassC
{
    public override bool CanConvert(Type typeToConvert) => typeof(TBase).IsAssignableFrom(typeToConvert);

    public override TBase 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 = Type.GetType(typeDiscriminator);
            if (type == null)
            {
                throw new JsonException($"Type {typeDiscriminator} could not be found.");
            }

            return (TBase)JsonSerializer.Deserialize(root.GetRawText(), type, options);
        }
    }

    public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
    }
}

public class Program
{
    public static void Main()
    {
        var options = new JsonSerializerOptions
        {
            Converters = { new PolymorphicJsonConverter<ClassC>() },
            WriteIndented = true
        };

        ClassA classA = new ClassA { PropertyA = "ValueA", PropertyB = "ValueB", PropertyC = "ValueC" };

        string json = JsonSerializer.Serialize<ClassC>(classA, options);
        ClassC deserialized = JsonSerializer.Deserialize<ClassC>(json, options);

        Console.WriteLine(json);
        Console.WriteLine(deserialized is ClassA); // Should output 'True'
    }
}

推荐答案

你的问题是你的转换器在这里递归地调用自己:

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)) // Here we guarantee there is no infinite recursion.
                throw new JsonException($"Type {typeDiscriminator} could not be found or is invalid.");

            return (ClassC?)root.Deserialize(type, options);
        }
    }

    public override void Write(Utf8JsonWriter writer, ClassC value, JsonSerializerOptions options) =>
        JsonSerializer.Serialize(writer, value, value.GetType(), options);
}

这个版本所做的假设是,任何可赋给ClassC的类型要么是抽象的(因此需要类型解析),要么是具体的(不需要类型解析).如果您有非抽象基类型,则此策略将不起作用,并且您需要采用更复杂的策略,在递归调用期间禁用转换器,如100中所示.

演示小提琴#1here.

.NET 7开始,对多态类型判别器的支持内置于System.Text.Json中,并且不需要转换器. this answer101 使用这种方法,您的类型可以应用以下属性进行往返:

[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; }
}

您可能需要修改传递给[JsonDerivedType(Type derivedType, string typeDiscriminator)]个属性构造函数的类型鉴别器值,以包括C#名称空间,如nameof(type) does not include the C# namespace.

here. history of life

Csharp相关问答推荐

错误NU 1301:无法加载源的服务索引

ListaryImportProperty的默认DllImportSearchPathsProperty行为

Unity如何在PlayerPrefs中保存数据?

Int和uint相乘得到LONG?

Blazor EventCallback<;MyType<;T>;>;

共享暂存/生产环境中Azure事件中心的建议配置

如何在ASP.NET Core8中启用REST应用程序的序列化?

每个http请求需要60秒,为什么?

如何在C#中创建VS代码中的控制台应用程序时自动生成Main方法

当使用Dapper映射DBNull时,我可以抛出异常吗?

使用C#和.NET 7.0无法访问Cookie中的数据

单元测试类型为HttpClient with Microsoft.Extensions.Http.Resilience

如何从非异步任务中正确返回TypeResult

岛屿和框架中的自定义控件库.Navigate-AccessViolationException

为什么我的属性即使没有显式地设置任何[必需]属性,也会显示验证?

ASP.NET Core 8 Web API:如何添加版本控制?

反序列化我以前使用System.Text.Json序列化的文件时出现异常

如何在.NET8中使用Blazor Web App(WebAssembly)托管服务器端控制器?

这是T自身的布尔表达式是什么意思?

C#Web服务转换为 node /Express不工作