System.Text.Json 反序列化为 object 类型时实际为 JsonElement 的方法

2024年7月10日 580点热度 0人点赞 0条评论
内容纲要

当一个字段为 object 类型时,System.Text.Json 自动设置 JsonElement 类型,而不是对应的类型,因此在很多情况下会出现奇怪的问题。

因此这里只要有两个地方加上代码,缓解这一情况。

第一步,实现转换器,当一个类型是 object 时,如果 json 是简单类型,则直接使用实际类型,而不是 JsonElement。

但是当字段是字典、数组,如 object[]T[] 但是 T 中有 object 字段 等情况时,这里是无效的。


public class ObjectJsonSerializer : IJsonSerializer
{
    private readonly JsonSerializerOptions _options;

    public ObjectJsonSerializer()
    {
        _options = new JsonSerializerOptions();
        _options.Converters.Add(new ObjectToInferredTypesConverter());
    }

    /// <inheritdoc />
    public TObject? Deserialize<TObject>(ReadOnlySpan<byte> bytes)
        where TObject : class
    {
        return System.Text.Json.JsonSerializer.Deserialize<TObject>(bytes, _options);
    }

    /// <inheritdoc />
    public byte[] Serializer<TObject>(TObject obj)
        where TObject : class
    {
        return System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(obj, _options);
    }
}

public class ObjectToInferredTypesConverter : JsonConverter<object>
{
    public override object Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options) => reader.TokenType switch
        {
            JsonTokenType.True => true,
            JsonTokenType.False => false,
            JsonTokenType.Number when reader.TryGetInt64(out long l) => l,
            JsonTokenType.Number => reader.GetDouble(),
            JsonTokenType.String when reader.TryGetDateTime(out DateTime datetime) => datetime,
            JsonTokenType.String => reader.GetString()!,
            _ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
        };

    public override void Write(
        Utf8JsonWriter writer,
        object objectToWrite,
        JsonSerializerOptions options) =>
         System.Text.Json.JsonSerializer.Serialize(writer, objectToWrite, objectToWrite.GetType(), options);
}

然后为了适配更多情况,只能手动转换,下面是扩展方法:


/// <summary>
/// 扩展.
/// </summary>
public static class JsonConvertExtensions
{
    /// <summary>
    /// 转换为对应类型.
    /// </summary>
    /// <param name="json"></param>
    /// <returns>object.</returns>
    public static object? ToObject(this JsonElement json)
    {
        switch (json.ValueKind)
        {
            case JsonValueKind.True:
                return json.GetBoolean();
            case JsonValueKind.False:
                return json.GetBoolean();
            case JsonValueKind.String:
                return json.GetString();
            case JsonValueKind.Null:
                return null;
            case JsonValueKind.Object:
                return json;
            case JsonValueKind.Number:
                return json.GetInt32();
        }

        return json;
    }

    /// <summary>
    /// 转换为对应类型.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns>object.</returns>
    public static object? ToObject(this object? obj)
    {
        if (obj == null)
        {
            return obj;
        }

        if (obj is JsonElement json)
        {
            switch (json.ValueKind)
            {
                case JsonValueKind.True:
                    return json.GetBoolean();
                case JsonValueKind.False:
                    return json.GetBoolean();
                case JsonValueKind.String:
                    return json.GetString();
                case JsonValueKind.Null:
                    return null;
                case JsonValueKind.Object:
                    return json;
                case JsonValueKind.Number:
                    return json.GetInt32();
            }

            return json;
        }

        return obj;
    }
}

对于数组等类型,只能手动使用扩展方法:

        for (int i = 0; i < message.Body.Data.Length; i++)
        {
            message.Body.Data[i].Value = message.Body.Data[i].Value.ToObject();
        }

file

痴者工良

高级程序员劝退师

文章评论