我尝试从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了解更多信息)。
TypeNameHandling
System.Text.Json
$type
允许有效负载指定自己的类型信息是Web应用程序中漏洞的常见来源。
然而, 是 通过创建一个以增加自己的多态反序列化方式支持JsonConverter<T>,所以在这个意义上说,这是可能的。
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。
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()); }