【问题标题】:Is it possible to serialize derived properties of list members in System.Text.Json?是否可以序列化 System.Text.Json 中列表成员的派生属性?
【发布时间】:2020-04-23 09:16:39
【问题描述】:

根据documentation of System.Text.Json

要序列化派生类型的属性,请使用以下方法之一:

  • (...)
  • 将要序列化的对象声明为object

不幸的是,正如我刚刚发现的那样,这不允许序列化列表成员的派生属性。考虑以下 DTO 类:

public abstract class GameUpdate
{
    public abstract string Type { get; }
}

public class ConsoleMessage : GameUpdate
{
    public override string Type => "ConsoleMessage";

    public string MessageContent { get; set; }
}

public class HitpointsUpdate : GameUpdate
{
    public override string Type => "HitpointsUpdate";

    public long MonsterID { get; set; }
    public long NewHP { get; set; }
}

它们应该用作List<GameUpdate>。不幸的是,将要序列化的列表声明为object 仍然无法序列化重要属性:

var gameUpdates = new List<GameUpdate>
{
    new ConsoleMessage { MessageContent = "Chimzee lost 10 HP!"},
    new HitpointsUpdate { MonsterID = 5, NewHP = 90 }
};
var json = System.Text.Json.JsonSerializer.Serialize<object>(gameUpdates);
Console.WriteLine(json);

这会产生以下 JSON:

[{"Type":"ConsoleMessage"},{"Type":"HitpointsUpdate"}]

为了比较,使用Newtonsoft.Json.JsonConvert.SerializeObject(gameUpdates) 会产生以下结果:

[{"Type":"ConsoleMessage","MessageContent":"Chimzee lost 10 HP!"},{"Type":"HitpointsUpdate","MonsterID":5,"NewHP":90}]

使用System.Text.Json是否可以达到类似的结果?

我问这个的原因:我一直在尝试开发一个基于浏览器的游戏作为一个爱好项目,而这正是我实现服务器与浏览器通信的方式。 Asp .NET Core 3 出来了its documentation encourages to migrate from Newtonsoft.JSON to System.Text.Json。在阅读了可以进行多态序列化的文档后,我删除了与 Newtonsoft 相关的代码并开始使用 System.Text.Json,却发现游戏不再工作。

【问题讨论】:

    标签: c# serialization .net-core .net-core-3.0 system.text.json


    【解决方案1】:

    这行得通:

    var gameUpdates = new List<object>
    {
        new ConsoleMessage { MessageContent = "Chimzee lost 10 HP!"},
        new HitpointsUpdate { MonsterID = 5, NewHP = 90 }
    };
    
    var json = JsonSerializer.Serialize(gameUpdates);
    Console.WriteLine(json);
    

    [{"Type":"ConsoleMessage","MessageContent":"Chimzee lost 10 HP!"},{"Type":"HitpointsUpdate","MonsterID":5,"NewHP":90}]

    旁注:您的方法可能很危险,您必须保留允许类型的白名单以再次反序列化,否则攻击者可以注入创建自己的类型...仅序列化一种类型的对象要安全得多在一个集合中。

    如果攻击者注入这样的代码怎么办:

    “类型”:“System.Drawing.Bitmap”,“宽度”:“100000”,“高度”:“100000”

    【讨论】:

    • 在 C# 方面,我验证预期的任何类型都可以从 JSON 提供的类型中分配 - 所以在这个例子中,我相信 System.Drawing.Bitmap 将无法通过此验证,因为 GameUpdate 是不能从System.Drawing.Bitmap 分配。幸运的是,在 JS 方面,这并不重要,有时动态类型似乎真的很方便。正如您所建议的那样,也许使用单一类型会更好,但我不知道如何做到这一点,因为不同的情况需要不同的更新使用不同的信息集
    • 另一个问题是,如果ConsoleMessageUpdate 包含遵循相同模式的属性——即存在抽象类ConsoleMessagePart 和派生类ConsoleMessageMonsterPartConsoleMessageTextPart、...和@ 987654330@ 包含属性List&lt;ConsoleMessagePart&gt; Parts {get; set;} (就是这种情况...)-我必须将此列表的类型切换为List&lt;object&gt;...嗯,这已经变得误导了,我想我要切换回JSON .NET。
    • 当序列化程序强制我更改代码并使其变得更糟(使用对象)时,该库已失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 2014-03-31
    • 1970-01-01
    • 2021-09-30
    相关资源
    最近更新 更多