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

2023-12-08

我尝试从 Newtonsoft.Json 迁移到 System.Text.Json。 我想反序列化抽象类。 Newtonsoft.Json 为此提供了 TypeNameHandling。 有没有办法通过 .net core 3.0 上的 System.Text.Json 反序列化抽象类?


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

答案是肯定的and不,取决于你的意思“可能的”.

no多态反序列化(相当于Newtonsoft.Json的TypeNameHandling) 支持built-in to System.Text.Json。这是因为读取 JSON 负载中指定为字符串的 .NET 类型名称(例如$type元数据属性)来创建您的对象是不建议因为它引入了潜在的安全问题(参见https://github.com/dotnet/corefx/issues/41347#issuecomment-535779492了解更多信息)。

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

然而,有is通过创建一个来添加您自己对多态反序列化的支持的方法JsonConverter<T>,所以从这个意义上来说,这是可能的。

该文档展示了如何使用类型鉴别器财产:https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization

让我们看一个例子。

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

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());
}

这是另一个 StackOverflow 问题,展示了如何使用接口(而不是抽象类)支持多态反序列化,但类似的解决方案适用于任何多态性:有没有一种简单的方法可以在 System.Text.Json 的自定义转换器中手动序列化/反序列化子对象?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

System.Text.Json 中可以进行多态反序列化吗? 的相关文章

随机推荐

  • Java MySQL的executeUpdate()对于INSERT ON ON DUPLICATE KEY UPDATE返回什么?

    我在网上查了大约3个小时 仍然找不到这个问题的答案 爪哇文档还有这个tutorial says 返回 1 SQL 数据操作语言的行计数 DML 语句或 2 0 表示不返回任何内容的 SQL 语句 那么这意味着 插入 1 行无重复项 gt 1
  • 尽管已安装,但未找到底图数据雇用

    我和这个帖子有同样的问题 使用辅助脚本中的导入来声明 var 可由另一个函数使用 但答案在我这边不起作用 对于上下文 basemap and basemap data hires已安装 但使用时resolution f 它会触发以下错误 O
  • 将 MJPEG 流式传输到文件,但仅保留最后 x 分钟

    我希望在检测到运动时记录 MJPEG 流 但我的运动检测通知比运动发生晚了几秒钟 为了解决这个问题 我想一直录制 但只保留最后 2 分钟的镜头 现在我正在使用 cURL 无限下载 但我一直在思考如何让它将文件的前面正确地修剪为 2 分钟 L
  • 纯CSS棋盘,带有div且没有类或id,可能吗?

    我有以下布局 div div div div div div div div div div div div div div div div div div div div div div div div div div div div d
  • 通知操作图标未显示

    我尝试在 Android 中显示通知 并使用来自这个链接 在一些消息来源中 人们说图标应该是全白色的 而一些消息来源说我应该使用 png代替vector 我尝试了所有这些方法 但没有人帮助我 我尝试这段代码 Notification new
  • 搜索数据库 JavaScript

    我已经消除了所有语法错误 但无法检索任何数据 任何帮助将不胜感激 db 变量存储我正在查找的视频数组 它是一个单独的 js 文件 数据库 var db JavaScript Version History http http wddbs c
  • 有人可以帮我使用 livestream 的 api 发出跨域 xml 请求吗?

    我正在尝试使用 livestream 非常有用的移动 api 位于http www livestream com userguide title Mobile API Requesting a mobile stream发出 xml 请求
  • 如何在 django 中为每个模型关联多种类型的标签

    我对 django 不太陌生 并试图找到最好的方法来做事 而不是自己编写所有内容 我正在开发一个模型 其中需要将多种类型的标签与模型关联 然后我想使用多个过滤条件检索对象 我看到在 django tagging 中 标签是按模型存储的 所以
  • 当两个应用程序都使用嵌入式 activemq 时,如何将 Jms 消息从一个 spring-boot 应用程序发送到另一个应用程序

    我有两个 spring boot 应用程序 在接收器应用程序的 Application java 中我有 Bean public JmsListenerContainerFactory
  • 在 WebAPI 中将 HttpResponseMessage 作为 excel 文件返回的问题

    我创建了 WebAPI 它使用 closexml nuget 返回一个 excel 文件 基本上它改变了我的DataTable脱颖而出 我指的是下面的几个链接 如何在 ASP NET WebAPI 中返回文件 FileContentResu
  • 在 Flutter 中访问 Firebase 存储

    我对 Flutter 相当陌生 以前从未使用过 Firebase 所以如果有明显的解决方案 我深表歉意 我正在开发一个 Flutter 应用程序 其中涉及记录表单提交并将其提交到中央位置 Firebase Storage 似乎很合适 因为据
  • 获取 GeoPandas 中几何图形之间的交集计数

    是否可以使用 GeoPandas 对象获取两个几何图形之间的交集计数 也就是说 我想计算一个 GeoDataFrame 中与另一个 GeoDataFrame 中的每个多边形相交的多边形或线串的数量 在浏览 GeoPandas 文档时 我没有
  • 寻找 C# 注册表类 [已关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 寻找包装调用以执行以下操作的 C 类 读取和写入键值 读取和写入密钥条目 枚举键中的条目 这个很重要 例如 需要列出以下位置的所有条目 HKEY L
  • 如何使用装饰器将变量注入作用域?

    免责声明 可能有更多的Python方式来做我想做的事情 但我想知道Python的作用域在这里是如何工作的 我正在尝试找到一种方法来制作一个装饰器 该装饰器可以执行诸如将名称注入另一个函数的作用域之类的操作 这样该名称就不会泄漏到装饰器的作用
  • 根据参考重新排序多索引数据框列

    我有一个多索引数据框 其名称附加到列级别 数据表看起来像这样 df1 TIME TMC 111N1 111P2 111N3 111P4 DATE EPOCH 0 143 113 103 NaN 1 183 NaN NaN NaN 2 NaN
  • CodeIgniter 与 PHPExcel 致命错误无法重新声明类 IOFactory

    我正在尝试将 PHPExcel 与 CodeIgniter 一起使用 我的问题是当我想使用下面的方法时 我得到了PHP 致命错误 无法重新声明类 IOFactory 如果您不确定文件类型 则可以在使用 createReader 方法实例化读
  • 在切片末尾工作是否惯用?

    我正在阅读 Go 的compress flate包 我发现了这段奇怪的代码 1 n int32 len list list list 0 n 1 list n maxNode 在上下文中 list保证指向后面有更多数据的数组 这是一个私有函
  • 如何在 Laravel PHP 框架中合并两个集合而不丢失(丢失)键?

    我是 Laravel PHP 的新手 我正在做我的个人 玩具项目 我遇到了一个我已经在 Google 上搜索了很长时间的问题 但是 我无法找出完美的解决方案 问题是 我有两个集合 questions and answers 我想将它们合并到
  • 用欧拉化求解中文Postman算法

    我想在不存在欧拉循环的图中解决中国邮递员问题 所以基本上我正在寻找图中的一条路径 该路径恰好访问每个边一次 并在同一节点处开始和结束 当且仅当每个节点具有相同数量的进入和离开图的边时 图才会有欧拉循环 显然我的图表没有 我发现欧拉化 制作欧
  • System.Text.Json 中可以进行多态反序列化吗?

    我尝试从 Newtonsoft Json 迁移到 System Text Json 我想反序列化抽象类 Newtonsoft Json 为此提供了 TypeNameHandling 有没有办法通过 net core 3 0 上的 Syste