使用递归类型反序列化json

时间:2016-01-23 07:31:15

标签: json.net

我有一些看起来像这样的json:

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

这可以用以下C#数据结构表示:

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

我尝试将其反序列化为

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

但它不起作用,因为它不知道Items成员是这个字典数据应该在递归调用上的位置。我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:2)

您的基本问题是您的JSON不代表MenuItem类列表的字典。相反,它代表MenuItem类的字典列表 - 与数据模型中的结构相反。

您可以通过多种方式表示和反序列化:

  1. MenuItem定义为MenuItem类型词典列表的子类:

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

    Json.NET将能够开箱即用反序列化:

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    
  2. 将您的MenuItem定义为包含MenuItem类型的词典列表:

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

    您需要一个自定义转换器,以便将Items冒充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);
        }
    }
    

    再次反序列化:

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    
  3. 如果您真的希望数据模型是列表而不是字典列表,则需要在读取和写入时重构数据模型:

    [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);
            }
        }
    }
    

    然后

    var root = JsonConvert.DeserializeObject<MenuItem>(json);
    
  4. 原型fiddle showing all three