簡體   English   中英

C# - 將嵌套的 json 反序列化為嵌套字典<string, object></string,>

[英]C# - Deserializing nested json to nested Dictionary<string, object>

我正在使用 .net 核心 3.1 和庫 System.Text.Json

How can I deserialize nested json object to Dictionary<string, object>, but the expectation is that based on json property type I'll get proper C# type:

String -> string
Number -> int/double
Object -> Dictionary<string, object>

默認情況下 - 如果我嘗試反序列化為 Dictionary<string, object> - 基本上每個 object 都是 JsonElement。 我希望它是上面提到的類型。

知道如何實現嗎?

為了將自由格式的 JSON 反序列化為 .Net 原始類型而不是JsonElement對象,您需要編寫自定義JsonConverter ,因為System.Text.Json沒有提供此類功能。

一種這樣的轉換器如下:

public class ObjectAsPrimitiveConverter : JsonConverter<object>
{
    FloatFormat FloatFormat { get; init; }
    UnknownNumberFormat UnknownNumberFormat { get; init; }
    ObjectFormat ObjectFormat { get; init; }

    public ObjectAsPrimitiveConverter() : this(FloatFormat.Double, UnknownNumberFormat.Error, ObjectFormat.Expando) { }
    public ObjectAsPrimitiveConverter(FloatFormat floatFormat, UnknownNumberFormat unknownNumberFormat, ObjectFormat objectFormat)
    {
        this.FloatFormat = floatFormat;
        this.UnknownNumberFormat = unknownNumberFormat;
        this.ObjectFormat = objectFormat;
    }
    
    public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
    {
        if (value.GetType() == typeof(object))
        {
            writer.WriteStartObject();
            writer.WriteEndObject();
        }
        else
        {
            JsonSerializer.Serialize(writer, value, value.GetType(), options);
        }
    }
    
    public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.Null:
                return null;
            case JsonTokenType.False:
                return false;
            case JsonTokenType.True:
                return true;
            case JsonTokenType.String:
                return reader.GetString();
            case JsonTokenType.Number:
            {
                if (reader.TryGetInt32(out var i))
                    return i;
                if (reader.TryGetInt64(out var l))
                    return l;
                // BigInteger could be added here.
                if (FloatFormat == FloatFormat.Decimal && reader.TryGetDecimal(out var m))
                    return m;
                else if (FloatFormat == FloatFormat.Double && reader.TryGetDouble(out var d))
                    return d;
                using var doc = JsonDocument.ParseValue(ref reader);
                if (UnknownNumberFormat == UnknownNumberFormat.JsonElement)
                    return doc.RootElement.Clone();
                throw new JsonException(string.Format("Cannot parse number {0}", doc.RootElement.ToString()));
            }
            case JsonTokenType.StartArray:
            {
                var list = new List<object>();
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        default:
                            list.Add(Read(ref reader, typeof(object), options));
                            break;
                        case JsonTokenType.EndArray:
                            return list;
                    }
                }
                throw new JsonException();
            }
            case JsonTokenType.StartObject:
                var dict = CreateDictionary();
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        case JsonTokenType.EndObject:
                            return dict;
                        case JsonTokenType.PropertyName:
                            var key = reader.GetString();
                            reader.Read();
                            dict.Add(key, Read(ref reader, typeof(object), options));
                            break;
                        default:
                            throw new JsonException();
                    }
                }
                throw new JsonException();
            default:
                throw new JsonException(string.Format("Unknown token {0}", reader.TokenType));
        }
    }
    
    protected virtual IDictionary<string, object> CreateDictionary() => 
        ObjectFormat == ObjectFormat.Expando ? new ExpandoObject() : new Dictionary<string, object>();
}

public enum FloatFormat
{
    Double,
    Decimal,
}

public enum UnknownNumberFormat
{
    Error,
    JsonElement,
}

public enum ObjectFormat
{
    Expando,
    Dictionary,
}

並使用它,反序列化為object (或dynamic ,如果配置為使用ExpandoObject )如下:

var options = new JsonSerializerOptions
{
    Converters = { new ObjectAsPrimitiveConverter(floatFormat : FloatFormat.Double, unknownNumberFormat : UnknownNumberFormat.Error, objectFormat : ObjectFormat.Expando) },
    WriteIndented = true,
};
dynamic d = JsonSerializer.Deserialize<dynamic>(json, options);

筆記:

  • JSON 允許任意精度和幅度的數字,而 .Net 原始數字類型則不允許。 在某些 JSON 數字無法解析為 .Net 原始類型的情況下,轉換器提供了為該數字返回JsonElement或拋出異常的選項。

    可以擴展轉換器以嘗試將不受支持的數字反序列化為BigInteger

  • 您可以將轉換器配置為對浮點數使用double精度或decimal ,對 JSON 對象使用Dictionary<string, object>ExpandoObject

演示小提琴在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM