代码之家  ›  专栏  ›  技术社区  ›  Zachary Turner

使用递归类型反序列化json

  •  2
  • Zachary Turner  · 技术社区  · 10 年前

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

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

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

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

    我尝试将其反序列化为

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

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

    1 回复  |  直到 10 年前
        1
  •  2
  •   dbc    10 年前

    您的基本问题是您的JSON不代表 MenuItem 课程。相反,它代表了 菜单项 类-与数据模型中的结构相反。

    有几种方法可以表示和反序列化:

    1. 定义您的 菜单项 作为字典列表的子类 菜单项 类型:

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

      杰森。NET将能够按开箱即用的方式对此进行反序列化:

      var root = JsonConvert.DeserializeObject<MenuItem>(json);
      
    2. 定义您的 菜单项 包含的字典列表 菜单项 类型:

      [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<菜单项>(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<菜单项>(json);
      

    原型 fiddle showing all three .