使用 System.Text.Json 反序列化复杂的多态类型 [英] Deserialize complex polymorphic types with System.Text.Json
本文介绍了使用 System.Text.Json 反序列化复杂的多态类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
文档中的 dotnet 示例:
The dotnet example in the documentation:
显示手动解析多态类型的每个属性.但是:
shows manually parsing each property of a polymorphic type. However:
- 我的多态对象是复杂的深层层次结构,我无法手动编写每个字段的代码,因此我需要调用
JsonSerializer
. - 类型的线索在同级字段中指定.鉴于无法保证 json 元素顺序,
Utf8JsonReader
在遇到多态类型之前可能没有读取类型信息.
- my polymorphic objects are complex deep hierarchies, I can't hand code every field so I need to invoke the
JsonSerializer
. - the clue for the type is specified in sibling fields. Given there is no guarantee about json element order, a
Utf8JsonReader
may not have read the type information before it encounters the polymorphic type.
例如
[JsonConverter(typeof(MessageConverter))]
public class Message
{
public string Type { get; set; } // indicates what implementation IBody is
public IBody Body { get; set; }
}
public interface IBody
{
}
public class BodyA : IBody
{
// a big object hierarchy but just showing one property for simplicity
public string A { get; set; }
}
public class BodyB : IBody
{
// a big object hierarchy but just showing one property for simplicity
public string B { get; set; }
}
public class MessageConverter : JsonConverter<Message>
{
public override bool CanConvert(Type objectType) =>
objectType == typeof(Message);
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var message = new Message();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "Type":
message.Type = reader.GetString();
break;
case "Body":
// Body might be read before "Message.Type" so can't parse it yet
message.Body = /* help - what am I? */;
break;
}
}
}
return message;
}
public override void Write(Utf8JsonWriter writer, Message value, JsonSerializerOptions options)
throw new NotImplementedException();
}
看Utf8JsonReader
:
- 有没有办法查看未来的元素或将解析器位置移回?
- 是否有一种有效的方法可以缓存部分 json 层次结构以进行延迟解析?
推荐答案
我目前的解决方案是,如有必要,使用 JsonDocument
缓存部分 json 以进行延迟解析.
The current solution I have is, if necessary, use a JsonDocument
to cache part of the json for deferred parsing.
我不喜欢的是我看不到在 JsonDocument
上调用 JsonSerializer
的方法,所以我必须使用 将其转换回文本GetRawText()
效率不高.
I don't like is that I can't see a way to invoke JsonSerializer
on a JsonDocument
so I have to convert it back to text with GetRawText()
which won't be very efficient.
public class MessageConverter : JsonConverter<Message>
{
public override bool CanConvert(Type objectType) =>
objectType == typeof(Message);
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var message = new Message();
JsonDocument cachedBody = null;
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "Type":
message.Type = reader.GetString();
break;
case "Body":
if (message.Type != null)
{
message.Body = message.Type switch
{
"A" => JsonSerializer.Deserialize<BodyA>(ref reader, options),
"B" => JsonSerializer.Deserialize<BodyB>(ref reader, options),
_ => throw new Exception($"Cannot parse message body of type {message.Type}")
};
}
else
{
cachedBody = JsonDocument.ParseValue(ref reader);
}
break;
}
}
}
if (message.Body == null)
{
if (cachedBody == null)
{
throw new Exception($"Missing message body");
}
try
{
Log.Write("using cache");
message.Body = message.Type switch
{
"A" => JsonSerializer.Deserialize<BodyA>(cachedBody.RootElement.GetRawText()),
"B" => JsonSerializer.Deserialize<BodyB>(cachedBody.RootElement.GetRawText()),
_ => throw new Exception($"Cannot parse message body of type {message.Type}")
};
}
finally
{
cachedBody.Dispose();
}
}
return message;
}
public override void Write(Utf8JsonWriter writer, Message value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName("Type");
writer.WriteStringValue(value.Type);
writer.WritePropertyName("Body");
JsonSerializer.Serialize<object>(writer, value.Body, options);
writer.WriteEndObject();
}
}
这篇关于使用 System.Text.Json 反序列化复杂的多态类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文