c# - System.Text.Json:如何使用接口(interface)属性反序列化类(.NET 6)

标签 c# .net json.net deserialization system.text.json

我目前正在将 vom Newtonsoft 迁移到 System.Text.Json。 Newtonsoft 能够自动反序列化具有一个或多个接口(interface)属性的对象。使用 System.Text.Json,当我尝试完成相同的任务时,我会收到相应类的以下错误消息:

反序列化构造函数中的每个参数必须绑定(bind)到反序列化时的对象属性或字段。每个参数名称必须与对象上的属性或字段匹配。

我可以通过编写自定义转换器来避免这个问题,但这会导致具有多个嵌套层的对象产生大量开销,其中每个 Interface 属性又可以拥有自己的多个 Interface 属性(因此需要多个自定义转换器) )。有没有更简单的解决方案来解决这个问题?

我创建一个示例来说明问题:

 [Fact]
        public void JsonTest()
        {

            var child = new Child(new Name("Peter"), 10);
            var parent = new Parent(child);

            var str = JsonSerializer.Serialize(parent);
            var jsonObj = JsonSerializer.Deserialize<Parent>(str);
            Console.WriteLine(jsonObj!.Child.Name);
        }
    }

    [JsonConverter(typeof(ParentConverter))]
    public class Parent
    {
        public Parent(Child child)
        {
            Child = child;
        }

        public IChild Child { get; set;}
    }

    [JsonConverter(typeof(ChildConverter))]
    public class Child : IChild
    {
        [JsonConstructor]
        public Child(Name name, int age)
        {
            Name = name;
            Age = age;
        }

        public IName Name { get; set; }
        public int Age { get; set; }
    }

    public class Name : IName
    {
        public string NameValue { get; set; }
        public Name(string nameValue)
        {
            NameValue = nameValue;
        }
    }

    public interface IChild
    {
        IName Name { get; }
        int Age { get; }
    }

    public interface IName
    {
        string NameValue { get; }
    }

    public class ParentConverter : JsonConverter<Parent>
    {
        public override Parent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            using (JsonDocument document = JsonDocument.ParseValue(ref reader))
            {
                JsonElement root = document.RootElement;

                if (root.TryGetProperty("Child", out JsonElement childElement))
                {
                    Child? child = JsonSerializer.Deserialize<Child>(childElement.GetRawText(), options);

                    return new Parent(child!);
                }
            }

            throw new JsonException("Invalid JSON data");
        }

        public override void Write(Utf8JsonWriter writer, Parent value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            writer.WritePropertyName("Child");
            JsonSerializer.Serialize(writer, value.Child, options);

            writer.WriteEndObject();
        }
    }

    public class ChildConverter : JsonConverter<Child>
    {
        public override Child Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            using (JsonDocument document = JsonDocument.ParseValue(ref reader))
            {
                var name = new Name("Alex");
                var age = 20;

                JsonElement root = document.RootElement;

                if (root.TryGetProperty("Name", out JsonElement nameElement))
                {
                    name = JsonSerializer.Deserialize<Name>(nameElement.GetRawText(), options);
                }

                if (root.TryGetProperty("Age", out JsonElement ageElement))
                {
                    age = JsonSerializer.Deserialize<int>(ageElement.GetRawText(), options);
                }
                return new Child(name!, age);
            }

            throw new JsonException("Invalid JSON data");
        }

        public override void Write(Utf8JsonWriter writer, Child value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();

            writer.WritePropertyName("Name");
            JsonSerializer.Serialize(writer, value.Name, options);

            writer.WritePropertyName("Age");
            JsonSerializer.Serialize(writer, value.Age, options);

            writer.WriteEndObject();
        }
    }

通过这些自定义转换器,我得到了预期的行为。但是有没有办法避免为每个这样的类编写自定义转换器?

最佳答案

这称为多态序列化。该库需要某种方式来注释属性实际上是什么 IChild 的具体实现。我认为 Newtonsoft 添加了一个具有完整类型名称的属性,虽然这很容易使用,但如果您想重命名您的类,它有一些潜在的缺点。

System.Text.Json 使用属性来注释派生类型,并要求您提供标识符。了解更多关于 Polymorphic type discriminators .

    [JsonDerivedType(typeof(Child1), typeDiscriminator: "Child1")]
    [JsonDerivedType(typeof(Child2), typeDiscriminator: "Child2")]
    public interface IChild
    {
        int Age { get; }
    }

    public class Child1 : IChild
    {
        public string PreferedToy { get; set; }
        public int Age { get; set; }
    }
    public class Child2 : IChild
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

如果每个接口(interface)只有一个实现,我会考虑要么放弃这些接口(interface),要么将对象转换为尽可能简单地序列化的数据传输对象 (DTO)。使用 DTO 有助于将序列化问题与类中的任何逻辑分开。

关于c# - System.Text.Json:如何使用接口(interface)属性反序列化类(.NET 6),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76529636/

相关文章:

.net - 使用 .NET 创建 TrueType 字体文件的子集

c# - 如何附加javascript文件并在构建dll文件时使用它们

c# - Json.Net反序列化内存不足问题

c# - 泛型的 JSON 序列化

c# - 将值 "AAAAAAAAB9c="转换为类型 'System.Version' 时出错。路径 'version'

c# - 使用 jQuery 而不是 AJAX 来模拟 UpdatePanel 功能

c# - 读入数据时对重复值求和

c# - 本应兼容的不兼容 .NET Standard 程序集?

.net - 我可以使用一些解决方法来避免混合内容错误吗?

c# - 如何在C#控制台应用程序(如Powershell)中传递自定义类型?