Select 在 System.Text.Json 中用于反序列化的类型

Select type to use for deserialization in System.Text.Json

我有一个 .NET 6 应用程序需要使用 System.Text.Json.

将一些 JSON 数据反序列化为 C# 对象

然而,在 classes 中我试图反序列化我有一个 classic 案例,其中 属性 有一个 base-class 类型,比如这个:

public class ClassToDeserialize
{
    public JobBase SomeJob { get; set; }
}

public class JobBase
{
    public string JobName { get; set; }
}

需要反序列化为正确的派生类型,例如:

public class OnDemandJob : JobBase
{
    public string RequestUser { get; set; }
}

public class ScheduledJob: JobBase
{
    public string Schedule { get; set; }
}

我想做的是编写一个自定义转换器来检查 JSON 数据以决定使用哪种类型进行反序列化(例如,在上面的示例中我们可以找出实际对象是否是通过检查数据中是否存在 Schedule 属性 然后反序列化为该类型 ScheduledJob 的实例。

现在,阅读官方文档我很高兴地发现 Microsoft 想到了这一点,并且有一篇文章包含如何使用转换器实现此场景的示例:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-6-0#support-polymorphic-deserialization

但是我的问题是:对我来说,他们在示例中实现的 Converter 对于它需要做的事情来说似乎过于复杂。特别是,他们在找到正确的类型后所做的是通过创建实例并手动读取 JSON 数据并将属性一一分配给对象来“手动”构建正确的派生对象。

这似乎是我想要实现的大量手动工作。直觉上,我认为应该可以做这样的事情(伪代码):

public JobBase Read(...)
{
    if(jsonObject.HasProperty("Schedule")
    {
        return JsonSerializer.Deserialize<ScheduledJob>(jsonString);
    }
    else 
    {
        return JsonSerializer.Deserialize<OnDemandJob>(jsonString);
    }
}

这样的事情可能吗?或者我真的必须逐个字段手动构建整个对象吗?

感谢@dbc 为我指明了正确的方向,这是我最终得到的转换器:

public class JobBaseConverter : JsonConverter<JobBase>
{
    public override JobBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException();
        }

        using (var jsonDocument = JsonDocument.ParseValue(ref reader))
        {
            if (jsonDocument.RootElement.TryGetProperty(nameof(ScheduledJob.Schedule), out var typeProperty))
            {
                return JsonSerializer.Deserialize<ScheduledJob>(jsonObject, options);
            }
            else
            {
                return JsonSerializer.Deserialize<OnDemandJob>(jsonObject, options);
            }
        }
    }

    public override void Write(Utf8JsonWriter writer, JobBase value, JsonSerializerOptions options)
    {
        if(value is ScheduledJob)
        {
            JsonSerializer.Serialize(writer, value as ScheduledJob, options);
        }
        else
        {
            JsonSerializer.Serialize(writer, value as OnDemandJob, options);
        }
    }
}