Deserializing json with recursive types

Deadly 提交于 2019-12-11 01:30:16

问题


I've got some json that looks like this:

[
  {
    "MenuItem1": [
      { "SubItem1": [ ] },
      { "SubItem2": [ ] },
      { "SubItem3": [ 
          { "SubSubItem": [ ] }
        ]
      }
    ]
  },
  { "MenuItem2": [ ] }
]

This can be represented with the following C# data structure:

class MenuItem
{
    Dictionary<string, MenuItem[]> Items;
}

I tried deserializing this as

MenuItem[] roots = JsonConvert.DeserializeObject<MenuItem[]>(json);

but it doesn't work because it doesn't know that the Items member is where this dictionary data is supposed to go on the recursive call. How can I make this work?


回答1:


Your basic problem is that your JSON does not represent a dictionary of lists of MenuItem classes. Instead, it represents a list of dictionaries of MenuItem classes - the reverse of the structure in your data model.

You have several ways you could represent and deserialize this:

  1. Define your MenuItem as a subclass of a list of dictionaries of MenuItem types:

    public class MenuItem : List<Dictionary<string, MenuItem>>
    {
    }
    

    Json.NET will be able to deserialize this as-is out of the box:

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    
  2. Define your MenuItem as containing a list of dictionaries of MenuItem types:

    [JsonConverter(typeof(MenuItemConverter))]
    class MenuItem
    {
        public Dictionary<string, MenuItem> [] Items;
    }
    

    You will need a custom converter to bubble the Items up one level in the JSON:

    public class MenuItemConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(MenuItem);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var items = serializer.Deserialize<Dictionary<string, MenuItem>[]>(reader);
            return new MenuItem { Items = items };
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var item = (MenuItem)value;
            serializer.Serialize(writer, item.Items);
        }
    }
    

    And to deserialize, once again:

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    
  3. If you really want your data model to be a dictionary of lists rather than a list of dictionaries, you will need to restructure your data model as you read and write it:

    [JsonConverter(typeof(MenuItemConverter))]
    class MenuItem
    {
        public MenuItem() { this.Items = new Dictionary<string, List<MenuItem>>(); }
        public Dictionary<string, List<MenuItem>> Items;
    }
    
    public static class DictionaryExtensions
    {
        public static void Add<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value)
            where TValueList : IList<TValue>, new()
        {
            if (listDictionary == null)
                throw new ArgumentNullException();
            TValueList values;
            if (!listDictionary.TryGetValue(key, out values))
                listDictionary[key] = values = new TValueList();
            values.Add(value);
        }
    
        public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(TKey key, TValue value)
        {
            var dict = new Dictionary<TKey, TValue>();
            dict[key] = value;
            return dict;
        }
    }
    
    public class MenuItemConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(MenuItem);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var items = serializer.Deserialize<Dictionary<string, MenuItem>[]>(reader);
            var menuItem = new MenuItem();
            foreach (var pair in items.SelectMany(d => d))
                menuItem.Items.Add(pair.Key, pair.Value);
            return menuItem;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var menuItem = (MenuItem)value;
            if (menuItem.Items == null)
                writer.WriteNull();
            else
            {
                var list = menuItem.Items.SelectMany(p => p.Value.Select(m => DictionaryExtensions.ToDictionary(p.Key, m)));
                serializer.Serialize(writer, list);
            }
        }
    }
    

    And then

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    

Prototype fiddle showing all three.



来源:https://stackoverflow.com/questions/34960853/deserializing-json-with-recursive-types

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!