Json.NET:反序列化嵌套字典

问题描述 投票:0回答:8

将对象反序列化为

Dictionary
(
JsonConvert.DeserializeObject<IDictionary<string,object>>(json)
) 时,嵌套对象将反序列化为
JObject
。是否可以强制将嵌套对象反序列化为
Dictionary
s?

c# json serialization json.net deserialization
8个回答
61
投票

我找到了一种通过提供

Dictionary<string,object>
实现将所有嵌套对象转换为
CustomCreationConverter
的方法:

class MyConverter : CustomCreationConverter<IDictionary<string, object>>
{
    public override IDictionary<string, object> Create(Type objectType)
    {
        return new Dictionary<string, object>();
    }

    public override bool CanConvert(Type objectType)
    {
        // in addition to handling IDictionary<string, object>
        // we want to handle the deserialization of dict value
        // which is of type object
        return objectType == typeof(object) || base.CanConvert(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject
            || reader.TokenType == JsonToken.Null)
            return base.ReadJson(reader, objectType, existingValue, serializer);

        // if the next token is not an object
        // then fall back on standard deserializer (strings, numbers etc.)
        return serializer.Deserialize(reader);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var json = File.ReadAllText(@"c:\test.json");
        var obj = JsonConvert.DeserializeObject<IDictionary<string, object>>(
            json, new JsonConverter[] {new MyConverter()});
    }
}

文档:CustomCreationConverter with Json.NET


14
投票

当我遇到这个问题时,我有一个非常相似但稍微复杂的需求。起初我想也许我可以调整接受的答案,但这看起来有点复杂,我最终采取了不同的方法。我试图将现代 JSON 层置于旧版 C++ API 之上。我就不详细介绍了,只是说要求归结为:

  • JSON 对象变成

    Dictionary<string,object>

  • JSON 数组变成

    List<object>

  • JSON 值成为相应的原始 CLR 值。

  • 对象和数组可以无限嵌套。

我首先将请求字符串反序列化为Newtonsoft JSON对象,然后调用我的方法按照上面的要求进行转换:

var jsonObject = JsonConvert.DeserializeObject(requestString);
var apiRequest = ToApiRequest(jsonObject);
// call the legacy C++ API ...

这是我转换为 API 期望的结构的方法:

    private static object ToApiRequest(object requestObject)
    {
        switch (requestObject)
        {
            case JObject jObject: // objects become Dictionary<string,object>
                return ((IEnumerable<KeyValuePair<string, JToken>>) jObject).ToDictionary(j => j.Key, j => ToApiRequest(j.Value));
            case JArray jArray: // arrays become List<object>
                return jArray.Select(ToApiRequest).ToList();
            case JValue jValue: // values just become the value
                return jValue.Value;
            default: // don't know what to do here
                throw new Exception($"Unsupported type: {requestObject.GetType()}");
        }
    }

我希望有人能发现这种方法有用。


2
投票
如果 json 中有数组,

@AlexD 接受的解决方案 效果并不理想。它返回

JArray
JObject
而不是
List<Dictionary<string, object>>

可以通过修改ReadJson()方法来解决:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
        return base.ReadJson(reader, objectType, existingValue, serializer);

    //if it's an array serialize it as a list of dictionaries
    if(reader.TokenType == JsonToken.ArrayStart)
        return serializer.Deserialize(reader, typeof(List<Dictionary<string, object>>));    


    // if the next token is not an object
    // then fall back on standard deserializer (strings, numbers etc.)
    return serializer.Deserialize(reader);
}

1
投票

替代/更新:

我需要反序列化

String
的字典,并且使用当前的 Json.NET (5.0),我不必创建 CustomConverter,我只是使用(在 VB.Net 中):

JsonConvert.DeserializeObject(Of IDictionary(Of String, IDictionary(Of String, String)))(jsonString)

或者,在 C# 中:

JsonConvert.DeserializeObject<IDictionary<String, IDictionary<String, String>>(jsonString);

1
投票

我有一个“未知”字典的嵌套/深层结构,它与 C# 对象和 JSON 字符串进行序列化/反序列化。 .NET 5.

如果我使用

Newtonsoft
,它不会自动工作。

如果我使用

System.Text.Json
它会自动工作。

//does NOT work (newtonDeserialized does not have the same data in the nested Dictionaries as in object):
var newtonSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(object);
var newtonDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<WaitlistResponse>(newtonSerialized);

//Works (netDeserialized have the same data in the nested Directories as in object):
var netSerialized = System.Text.Json.JsonSerializer.Serialize(object);
var netDeserialized = System.Text.Json.JsonSerializer.Deserialize<WaitlistResponse>(netSerialized);

1
投票

就我而言,并非所有内容都是嵌套字典。我还有一个数组,它是基本类型的键值对,当数组的对象不是字典时它会抛出异常。

所以,根据Phillip S的回答,我想出了

public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
            JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
                return base.ReadJson(reader, objectType, existingValue, serializer);

            //if it's an array serialize it as a list of dictionaries
            if (reader.TokenType == JsonToken.StartArray)
            {
                return serializer.Deserialize(reader, typeof(List<object>)); // instead of List<Dictionary<string, object>>
            }

            // if the next token is not an object
            // then fall back on standard deserializer (strings, numbers etc.)
            return serializer.Deserialize(reader);
        }

希望它能帮助那些还没有工作的人。


1
投票

首先,您需要序列化字典值。

var settings = new JsonSerializerSettings { TypeNameHandling= TypeNameHandling.All };

var serializeValues = JsonConvert.SerializeObject(nestedDictionaryValues, settings);

//After serialize using deserialize using the same TypeNameHandling.

var deserializeValues = JsonConvert.DeserializeObject(serializeValues, settings);

0
投票

@AlexD 和 @Phillp 之间的组合解决方案可以让您获得 JavaScriptSerializer 的原始行为,其中包括将嵌套对象反序列化为字典(String,Object)和将数组反序列化为包含字典(String,Object)的对象数组。遗憾的是 .NET 6/7/8/9 废除了该库并强制更改为 System.Text.Json。我花了一整天的时间对包含 JObject (Newtonsoft) 或 JsonElement (Microsoft) 的结果感到恼火。

使用自定义转换器,您可以处理嵌套对象和数组。此外,默认情况下,JsonSerializerSettings 会将日期/日期时间的字符串值更改为实际对象(日期)。非常烦人,因为字符串应该反序列化为字符串,而不是解释并转换为日期。但幸运的是,有一个选项可以禁用它。您必须将 DateParseHandling 应用于读取器和序列化器。

Class CustomJSONConverter : CustomCreationConverter<IDictionary<string, object>>
{
    public override IDictionary<string, object> Create(Type objectType)
    {
        return new Dictionary<string, object>();
    }
    
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(object) || base.CanConvert(objectType);
    }
    
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        serializer.DateParseHandling = DateParseHandling.None;
        reader.DateParseHandling = DateParseHandling.None;
        if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
            return base.ReadJson(reader, objectType, existingValue, serializer);
        if (reader.TokenType == JsonToken.StartArray)
            return serializer.Deserialize(reader, typeof(object[]));
        return serializer.Deserialize(reader);
    }
}

Class MainClass
{
    private void parseJsonString(string jsonString)
    {
        Dictionary<string, object> dictionary = null;
        dictionary = JsonConvert.DeserializeObject<IDictionary<string, object>>(jsonString, new JsonConverter[] { new CustomJSONConverter() }) as Dictionary<string, object>;

        if (dictionary != null)
        {
            parseDictionary(dictionary);
            return;
        }
    }

    private void parseDictionary(Dictionary<string, object> dictionary)
    {
        foreach (KeyValuePair<string, object> item in dictionary)
        {
            if (item.Value is Array)
                //Handle Array
            else
                //Handle Value
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.