C#如何反序列化派生类

前言

上回,我们讲解了《如何序列化派生类》。

那如何反序列化派生类呢?

假设有一个 Person 抽象基类,其中包含 Student 和 Teacher 派生类:.

public class Person
{
    public string Name { get; set; }
}

public class Student : Person
{
    public int Score { get; set; }
}

public class Teacher : Person
{
    public string Title { get; set; }
}

如果 API 输入类型是单个 Person 抽象基类,即使我们传入正确格式的派生类 JSON 字符串,System.Text.Json也只会使用基类进行反序列化:

[HttpPost]
[Route("get")]
public string Get(Person person)
{
    return person.GetType().ToString();
}

C#如何反序列化派生类

我们必须主动告诉System.Text.Json如何处理反序列化派生类,因此需要自定义转换器。

思路

与序列化相反,我们需要实现自定义转换器的Read方法:

public class PersonConverter : JsonConverter<Person>
{
    public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        
    }
}

现在的关键是,如何判断到底要反序列化成哪个派生类型。

我们可以使用Read方法遍历 JSON 的所有 Property, 找到对应派生类型独有的属性,即可知道当前需要反序列化成哪个派生类型。

实现

为基类创建自定义转换器,实现Read方法:

public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    Utf8JsonReader readerClone = reader;
    while (readerClone.Read())
    {
        JsonTokenType tokenType = readerClone.TokenType;

        switch (tokenType)
        {
            case JsonTokenType.PropertyName:
                if (readerClone.ValueTextEquals("score"))
                {
                    return (Person)JsonSerializer.Deserialize(ref reader,typeof(Student), options);
                }
                else if (readerClone.ValueTextEquals("title"))
                {
                    return (Person)JsonSerializer.Deserialize(ref reader, typeof(Teacher), options);
                }
                break;
        }
    }

    throw new NotImplementedException();
}

因为 Utf8JsonReader 是只进读取器,因此这里需要创建 Utf8JsonReader 实例的克隆readerClone去遍历 JSON,而原始 reader 用于反序列化派生类。

然后修改 Startup.cs 文件,注册自定义转换器:

services.AddControllers()
    .AddJsonOptions(options =>
        options.JsonSerializerOptions.Converters.Add(new PersonConverter()));

运行,反序列化成功:

C#如何反序列化派生类

结论

通过自定义转换器,我们实现了使用System.Text.Json反序列化派生类。