是否可以序列化 System.Text.Json 中列表成员的派生属性?
Is it possible to serialize derived properties of list members in System.Text.Json?
根据 documentation of System.Text.Json
:
To serialize the properties of the derived type, use one of the following approaches:
- (...)
- Declare the object to be serialized as
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,结果发现游戏不再运行了。
这个有效:
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}]
旁注:您的方法可能很危险,您必须保留允许类型的白名单以再次反序列化,否则攻击者可能会注入他自己类型的创建...仅序列化一种类型会更安全一个集合中的对象。
如果攻击者注入这样的代码怎么办:
"Type":"System.Drawing.Bitmap","Width":"100000", "Height":"100000"
根据 documentation of System.Text.Json
:
To serialize the properties of the derived type, use one of the following approaches:
- (...)
- Declare the object to be serialized as
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,结果发现游戏不再运行了。
这个有效:
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}]
旁注:您的方法可能很危险,您必须保留允许类型的白名单以再次反序列化,否则攻击者可能会注入他自己类型的创建...仅序列化一种类型会更安全一个集合中的对象。
如果攻击者注入这样的代码怎么办:
"Type":"System.Drawing.Bitmap","Width":"100000", "Height":"100000"