当前位置: 首页 > 面试题库 >

System.Text.Json中可以进行多态反序列化吗?

章宏恺
2023-03-14
问题内容

我尝试从Newtonsoft.Json迁移到System.Text.Json。我想反序列化抽象类。Newtonsoft.Json为此具有TypeNameHandling。有什么方法可以通过.net
core 3.0上的System.Text.Json反序列化抽象类?


问题答案:

System.Text.Json中可以进行多态反序列化吗?

答案是肯定的 ,并 没有,这取决于你的意思是什么 “可能”

没有 多态的反序列化(相当于Newtonsoft.Json的TypeNameHandling)支持 内置
System.Text.Json。这是因为阅读指定为JSON有效载荷(如在一个字符串的.NET类型名称$type是元数据属性)来创建你的对象
推荐使用 ,因为它引入了潜在的安全隐患(见https://github.com/dotnet/corefx/ Issues /
41347#issuecomment-535779492了解更多信息)。

允许有效负载指定自己的类型信息是Web应用程序中漏洞的常见来源。

然而, 通过创建一个以增加自己的多态反序列化方式支持JsonConverter<T>,所以在这个意义上说,这是可能的。

该文档显示了一个使用 类型鉴别器 属性的方法的示例:https : //docs.microsoft.com/zh-
cn/dotnet/standard/serialization/system-text-json-converters-how-to#support-
多态反序列化

让我们来看一个例子。

假设您有一个基类和几个派生类:

public class BaseClass
{
    public int Int { get; set; }
}
public class DerivedA : BaseClass
{
    public string Str { get; set; }
}
public class DerivedB : BaseClass
{
    public bool Bool { get; set; }
}

您可以创建以下代码JsonConverter<BaseClass>,该代码在序列化时写入类型识别符,然后读取以识别要反序列化的类型。您可以在上注册该转换器JsonSerializerOptions

public class BaseClassConverter : JsonConverter<BaseClass>
{
    private enum TypeDiscriminator
    {
        BaseClass = 0,
        DerivedA = 1,
        DerivedB = 2
    }

    public override bool CanConvert(Type type)
    {
        return typeof(BaseClass).IsAssignableFrom(type);
    }

    public override BaseClass Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.StartObject)
        {
            throw new JsonException();
        }

        if (!reader.Read()
                || reader.TokenType != JsonTokenType.PropertyName
                || reader.GetString() != "TypeDiscriminator")
        {
            throw new JsonException();
        }

        if (!reader.Read() || reader.TokenType != JsonTokenType.Number)
        {
            throw new JsonException();
        }

        BaseClass baseClass;
        TypeDiscriminator typeDiscriminator = (TypeDiscriminator)reader.GetInt32();
        switch (typeDiscriminator)
        {
            case TypeDiscriminator.DerivedA:
                if (!reader.Read() || reader.GetString() != "TypeValue")
                {
                    throw new JsonException();
                }
                if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
                {
                    throw new JsonException();
                }
                baseClass = (DerivedA)JsonSerializer.Deserialize(ref reader, typeof(DerivedA));
                break;
            case TypeDiscriminator.DerivedB:
                if (!reader.Read() || reader.GetString() != "TypeValue")
                {
                    throw new JsonException();
                }
                if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
                {
                    throw new JsonException();
                }
                baseClass = (DerivedB)JsonSerializer.Deserialize(ref reader, typeof(DerivedB));
                break;
            default:
                throw new NotSupportedException();
        }

        if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject)
        {
            throw new JsonException();
        }

        return baseClass;
    }

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

        if (value is DerivedA derivedA)
        {
            writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedA);
            writer.WritePropertyName("TypeValue");
            JsonSerializer.Serialize(writer, derivedA);
        }
        else if (value is DerivedB derivedB)
        {
            writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedB);
            writer.WritePropertyName("TypeValue");
            JsonSerializer.Serialize(writer, derivedB);
        }
        else
        {
            throw new NotSupportedException();
        }

        writer.WriteEndObject();
    }
}

这是序列化和反序列化的样子(包括与Newtonsoft.Json的比较):

private static void PolymorphicSupportComparison()
{
    var objects = new List<BaseClass> { new DerivedA(), new DerivedB() };

    // Using: System.Text.Json
    var options = new JsonSerializerOptions
    {
        Converters = { new BaseClassConverter() },
        WriteIndented = true
    };

    string jsonString = JsonSerializer.Serialize(objects, options);
    Console.WriteLine(jsonString);
    /*
     [
      {
        "TypeDiscriminator": 1,
        "TypeValue": {
            "Str": null,
            "Int": 0
        }
      },
      {
        "TypeDiscriminator": 2,
        "TypeValue": {
            "Bool": false,
            "Int": 0
        }
      }
     ]
    */

    var roundTrip = JsonSerializer.Deserialize<List<BaseClass>>(jsonString, options);


    // Using: Newtonsoft.Json
    var settings = new Newtonsoft.Json.JsonSerializerSettings
    {
        TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
        Formatting = Newtonsoft.Json.Formatting.Indented
    };

    jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(objects, settings);
    Console.WriteLine(jsonString);
    /*
     [
      {
        "$type": "PolymorphicSerialization.DerivedA, PolymorphicSerialization",
        "Str": null,
        "Int": 0
      },
      {
        "$type": "PolymorphicSerialization.DerivedB, PolymorphicSerialization",
        "Bool": false,
        "Int": 0
      }
     ]
    */

    var originalList = JsonConvert.DeserializeObject<List<BaseClass>>(jsonString, settings);

    Debug.Assert(originalList[0].GetType() == roundTrip[0].GetType());
}


 类似资料:
  • 我对Jackson和类型层次结构有以下问题。我正在序列化一个类SubA,该类将扩展为一个字符串,然后尝试将其反序列化回来。当然,在编译时,系统不知道它是基还是SubA,因此我希望它是基,如果它是SubA,则会在编译后执行一些其他操作。 我的基本类看起来像: ...和一个派生自的类: ... 我试图执行以下代码: String是: 但我一次又一次地犯同样的错误。映射器现在知道如何处理另一个类参数-它

  • 我想为我们的REST API实现一个自定义的反序列化器,它不仅被Java应用程序使用。因此,我不想让Jackson将类型信息放入序列化的JSON中。 我目前正在努力反序列化<code>CollectionExpand</code>,因为它包含特定<code>ResourceModel</code>的<code>数据</code>列表。 < code>ResourceModel是一个接口,每个< c

  • 我有一个接口,其中和实现。服务器会返回这样的响应- 相应的类来建模它是 中的声明它是哪种车辆。我需要反序列化的成一个对象取决于。如果是,我需要在反序列化和期间使用反序列化。 如何使用gson实现这一点? 编辑-这篇文章的不同之处在于类类型()包含在需要反序列化的中。这里不是。如果在内,我可以为编写自定义反序列化程序,检查,并进行相应的反序列化。但是我想我需要为编写一个自定义反序列化程序,在这里我创

  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器

  • Jackson版本:2.10.1 谢谢你的回答!

  • 我的JSON字符串是: 我想要实现的是,当JSON中没有提供schemaVersion时,能够在默认情况下反序列化到SubClassV1,但即使在Superclass中将schemaVersion初始化为“1.0”时,我仍然会收到以下错误: