问题描述
我尝试从Newtonsoft.Json迁移到System.Text.Json.我想反序列化抽象类. Newtonsoft.Json为此具有TypeNameHandling.是否可以通过.net core 3.0上的System.Text.Json反序列化抽象类?
I try to migrate from Newtonsoft.Json to System.Text.Json.I want to deserialize abstract class. Newtonsoft.Json has TypeNameHandling for this.Is there any way to deserialize abstract class via System.Text.Json on .net core 3.0?
推荐答案
答案是是和否,这取决于您可能" 的意思.
The answer is yes and no, depending on what you mean by "possible".
System.Text.Json
没有无多态反序列化(相当于Newtonsoft.Json的TypeNameHandling
)对System.Text.Json
的内置支持.这是因为不建议阅读,因为不建议读取JSON有效负载(例如$type
元数据属性)中指定为字符串的.NET类型名称来创建对象,因为这会带来潜在的安全隐患(请参阅href ="https://github.com/dotnet/corefx/issues/41347#issuecomment-535779492" rel ="noreferrer"> https://github.com/dotnet/corefx/issues/41347#issuecomment-535779492 有关更多信息).
There is no polymorphic deserialization (equivalent to Newtonsoft.Json's TypeNameHandling
) support built-in to System.Text.Json
. This is because reading the .NET type name specified as a string within the JSON payload (such as $type
metadata property) to create your objects is not recommended since it introduces potential security concerns (see https://github.com/dotnet/corefx/issues/41347#issuecomment-535779492 for more info).
但是,有 种方法可以通过创建JsonConverter<T>
来添加自己对多态反序列化的支持,因此从某种意义上说,这是可能的.
However, there is a way to add your own support for polymorphic deserialization by creating a JsonConverter<T>
, so in that sense, it is possible.
文档显示了如何使用类型鉴别器属性来执行此操作的示例: https ://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization
The docs show an example of how to do that using a type discriminator property:https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization
我们来看一个例子.
假设您有一个基类和几个派生类:
Say you have a base class and a couple of derived classes:
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
上注册该转换器.
You can create the following JsonConverter<BaseClass>
that writes the type discriminator while serializing and reads it to figure out which type to deserialize. You can register that converter on the 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的比较):
This is what serialization and deserialization would look like (including comparison with 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中的自定义转换器中手动序列化/反序列化子对象的简单方法?
Here's another StackOverflow question that shows how to support polymorphic deserialization with interfaces (rather than abstract classes), but a similar solution would apply for any polymorphism:Is there a simple way to manually serialize/deserialize child objects in a custom converter in System.Text.Json?
这篇关于System.Text.Json中是否可以进行多态反序列化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!