0

文档中的 dotnet 示例:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#support-polymorphic-deserialization

显示手动解析多态类型的每个属性。然而:

  • 我的多态对象是复杂的深层层次结构,我不能手动编写每个字段的代码,所以我需要调用JsonSerializer.
  • 类型的线索在同级字段中指定。鉴于无法保证 json 元素顺序,aUtf8JsonReader可能在遇到多态类型之前还没有读取类型信息。

例如

[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 层次结构以进行延迟解析?
4

1 回答 1

0

我目前的解决方案是,如有必要,使用 aJsonDocument缓存部分 json 以进行延迟解析。

我不喜欢的是我看不到JsonSerializer在 a上调​​用的方法,JsonDocument因此我必须将其转换回GetRawText()效率不高的文本。

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();
    }
}
于 2020-07-17T07:24:46.297 回答