掌握System.Text.Json中的多态序列化和反序列化

作者:微信公众号:【架构师老卢】
1-26 15:34
579

该库包含在 .NET Core 3.1 及更高版本的运行时中。System.Text.Json 比常用的 Newtonsoft.Json 快得多。System.Text.Json

但是,System.Text.Json 和 Newtonsoft.Json 之间存在一些重大更改。Microsoft 的此页面列出了它们之间的所有差异。多态序列化/反序列化就是其中之一。

在 Newtonsoft.Json 中,会自动处理多态序列化。尽管没有对多态反序列化的开箱即用支持,但可以使用 nuget 包 JsonSubTypes 来执行此操作。有关如何将 JsonSubTypes 与 Newtonsoft.Json 一起使用的详细信息。

但是,在 System.Text.Json 中,不直接支持多态序列化/反序列化。Microsoft 站点中介绍了几种解决方法。本文将重点介绍如何使用自定义 JsonConverter 实现多态序列化/反序列化。

首先,让我们定义具有继承关系的模型。

  1. BasePet 是基抽象类。在这个类中,我们需要定义一个名为“TypeDiscriminator”的字符串类型属性。此属性将用于告诉序列化/反序列化过程类的 pet 类型。它还具有属性 [JsonConverter(typeof(PetConverter))]。此属性告诉框架在遇到此类数据时要使用的自定义转换器。在此之后,我们将讨论自定义转换器。
  2. Cat 和 Dog 是从 BasePet 类继承的子类。
  3. Person 类包含 BasePet 类型的属性“Pet”。在运行时,此“Pet”属性可以是 Cat 或 Dog 对象。这就是OOP中的多态性。
using System.Text.Json.Serialization;
using SystemTextJsonPolymorphism;

namespace IO.Swagger.Model
{
    /// <summary>
    /// Person
    /// </summary>
    public partial class Person
    {
        public Person() { }
        /// <summary>
        /// Gets or Sets Pet
        /// </summary>
        [JsonPropertyName("pet")]
        public BasePet Pet { get; set; }
        /// <summary>
        /// Gets or Sets FullName
        /// </summary>
        [JsonPropertyName("fullName")]
        public string FullName { get; set; }
    }
    /// <summary>
    /// BasePet
    /// </summary>
    [JsonConverter(typeof(PetConverter))]
    public abstract class BasePet
    {
        public BasePet() { }
        /// <summary>
        /// Gets or Sets Name
        /// </summary>
        [JsonPropertyName("name")]
        public string Name { get; set; }
        /// <summary>
        /// Gets or Sets PetType
        /// </summary>
        [JsonPropertyName("typeDiscriminator")]
        public string TypeDiscriminator { get; set; }
    }
    /// <summary>
    /// Cat
    /// </summary>
    public partial class Cat : BasePet
    {
        public Cat() { }
        /// <summary>
        /// Gets or Sets Meow
        /// </summary>
        [JsonPropertyName("meow")]
        public string Meow { get; set; }
    }
    /// <summary>
    /// Dog
    /// </summary>
    public partial class Dog : BasePet
    {
        public Dog() { }
        /// <summary>
        /// Gets or Sets Bark
        /// </summary>
        [JsonPropertyName("bark")]
        public string Bark { get; set; }
    }
}

具有继承关系的对象 接下来,我们将定义一个继承自 JsonConverter 基类的 PetConverter 类。在本类中,我们需要重写 3 个方法:

  1. public 覆盖 bool CanConvert(Type typeToConvert) => typeof(BasePet)。IsAssignableFrom(typeToConvert):此方法将指示提供的类型是否有效(如果它是 BasePet 的子类)。
  2. public override BasePet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options):当 Json 反序列化进程遇到 Json 字符串中的“pet”属性时,调用此方法。调用时,读取器当前游标指向第一个字符作为“pet”属性值。在我们的例子中,它应该是“{”(对象的开头)。在这个方法中,我们读取“TypeDiscriminator”属性值,并根据该值调用 JsonSerializer.Deserialize() 方法将 Json 字符串值反序列化为相应的子类。
  3. public override void Write(Utf8JsonWriter writer, BasePet pet, JsonSerializerOptions options):当 Json 序列化进程遇到 Person 对象中的“Pet”属性时,调用此方法。然后,它会检查 Pet 属性的真正子类型,并调用 JsonSerializer.Serialize() 方法对其进行相应的序列化。
using IO.Swagger.Model;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonPolymorphism
{
    internal class PetConverter : JsonConverter<BasePet>
    {
        public override bool CanConvert(Type typeToConvert) => typeof(BasePet).IsAssignableFrom(typeToConvert);
        public override BasePet 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("typeDiscriminator", out var typeProperty))
                {
                    throw new JsonException();
                }
                var jsonPet = jsonDocument.RootElement.GetRawText();
                switch (typeProperty.GetString())
                {
                    case "Cat":
                        return (Cat)JsonSerializer.Deserialize(jsonPet, typeof(Cat));
                    case "Dog":
                        return (Dog)JsonSerializer.Deserialize(jsonPet, typeof(Dog));
                    default:
                        throw new JsonException();
                };
            }
        }
        public override void Write(
        Utf8JsonWriter writer, BasePet pet, JsonSerializerOptions options)
        {
            if (pet is Cat cat)
            {
                JsonSerializer.Serialize(writer, cat);
            }
            else if (pet is Dog dog)
            {
                JsonSerializer.Serialize(writer, dog);
            }
        }
    }
}

这就是我们在 System.Text.Json 中实现多态序列化和反序列化所需要做的全部工作。以下代码片段显示了我们如何使用它和结果。

using System;
using IO.Swagger.Model;
using System.Text.Json;

namespace ConsoleAppNetCore
{
    class Program
    {
        static void Main(string[] args)
        {
            const string jsonPerson1 = @"{
                ""fullName"": ""John Smith1"",
                ""Pet"": {
                    ""name"": ""PetCat"",
                    ""typeDiscriminator"": ""Cat"",
                    ""meow"": ""Meow""
                }
            }";
            var options = new JsonSerializerOptions
            {
                WriteIndented = true,
                PropertyNameCaseInsensitive = true,
            };
            var person1 = new Person()
            {
                FullName = "John Smith1",
                Pet = new Cat()
                {
                    Name = "PetCat",
                    TypeDiscriminator = "Cat",
                    Meow = "Meow"
                }
            };
            var person2 = new Person()
            {
                FullName = "John Smith2",
                Pet = new Dog()
                {
                    Name = "PetDog",
                    TypeDiscriminator = "Dog",
                    Bark = "Bark"
                }
            };
            var jsonString1 = JsonSerializer.Serialize(person1, options);
            var jsonString2 = JsonSerializer.Serialize(person2, options);
            Console.WriteLine(jsonString1);
            Console.WriteLine(jsonString2);
            var person1_1 = JsonSerializer.Deserialize<Person>(jsonPerson1);
            var person2_1 = JsonSerializer.Deserialize<Person>(jsonString2);
            jsonString1 = JsonSerializer.Serialize(person1, options);
            jsonString2 = JsonSerializer.Serialize(person2, options);
            Console.WriteLine(jsonString1);
            Console.WriteLine(jsonString2);
            Console.ReadKey();
        }
    }
}

以下是运行 Program.cs 的结果。它演示了 pet 属性(Cat 或 Dog 类型)可以成功序列化和反序列化。

相关留言评论
昵称:
邮箱:
阅读排行