简体   繁体   English

如何将分层数据从DataTable转换为JSON

[英]How to convert hierarchical data from a DataTable to JSON

I have a hierarchical data table as follows which generates a menu and its sub-menus. 我有一个如下的分层数据表,它生成一个菜单及其子菜单。 The main menu has a ParentId of 0 . 主菜单的ParentId0 Each sub-menu has a ParentId referring to the ResourceId of another row in the table. 每个子菜单都有一个ParentId引用表中另一行的ResourceId

ResourceId  DisplayName    ParentId    Url
-----------------------------------------------
1           Home           0           Some Url
2           Student        0           Some Url
3           Staff          0           Some Url
4           Library        0           Some Url
6           StudentAtt     1           Some Url
7           TimeTable      1           Some Url
8           Staff Att      2           Some Url
9           Book Issue     3           Some Url
10          Book Return    3           Some Url
11          Fee Payment    4           Some Url
12          Book fine      10          Some Url

I need to convert the data to JSON. 我需要将数据转换为JSON。 Below is the code I tried out. 下面是我试过的代码。 I am trying to check if ParentId of the sub-menu equals ResourceId of the main menu. 我试图检查子菜单的ParentId是否等于主菜单的ResourceId But the sub-menu is not displayed. 但是不显示子菜单。 (The variable table is a DataTable .) (变量tableDataTable 。)

    var rows = table.Rows.Cast<DataRow>().ToList();
    var result = rows
        .Where(x => x["ParentId"].ToString() == "0")
        .GroupBy(r => new { x = r["ResourceId"] })
        .Select(g => new
        {
            //MenuLevel = g.Key.x,
            MenuDetails = g
                .GroupBy(r => new
                {
                    a = r["DisplayName"],
                    b = r["Url"]
                })
                .Select(detail => new
                {
                    DisplayName = detail.Key.a,
                    Url = detail.Key.b,
                    SubMenu = detail
                        .Where(y => g.Key.x.ToString() == y["ParentId"].ToString())
                        .GroupBy(r => new 
                        { 
                            f = r["DisplayName"] 
                        })
                        .Select(subMenu => new
                        {
                            SubMenuDisplayName = subMenu.Key.f
                        })
                })
        });

The result I got is below: 我得到的结果如下:

[
    {
        "MenuDetails": [
            {
                "DisplayName": "Home",
                "Url": null,
                "SubMenu": []
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Student",
                "Url": null,
                "SubMenu": []
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Staff",
                "Url": null,
                "SubMenu": []
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Library",
                "Url": null,
                "SubMenu": []
            }
        ]
    }
]

But the expected result is: 但预期的结果是:

[
    {
        "MenuDetails": [
            {
                "DisplayName": "Home",
                "Url": null,
                "SubMenu": [
                    {
                        "SubMenuDisplayName": "StudentAtt"
                    },
                    {
                        "SubMenuDisplayName": "TimeTable"
                    }
                ]
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Student",
                "Url": null,
                "SubMenu": [
                    {
                        "SubMenuDisplayName": "Staff Att"
                    }
                ]
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Staff",
                "Url": null,
                "SubMenu": [
                    {
                        "SubMenuDisplayName": "Book Issue"
                    },
                    {
                        "SubMenuDisplayName": "Book Return"
                    }
                ]
            }
        ]
    },
    {
        "MenuDetails": [
            {
                "DisplayName": "Library",
                "Url": null,
                "SubMenu": [
                    {
                        "SubMenuDisplayName": "Fee Payment "
                    }
                ]
            }
        ]
    }
]

I also need to display the sub-sub-menu (which has its ParentId pointing to the ResourceId of the sub-menu). 我还需要显示子子菜单(其ParentId指向子菜单的ResourceId )。

The "expected" JSON you posted in your question is not a fully recursive structure, because it is inconsistent from level to level: the submenu items use a different property for the display name than the top menu items, and they do not have URLs or submenu collections themselves. 您在问题中发布的“预期”JSON不是完全递归的结构,因为它在级别之间不一致:子菜单项使用与顶级菜单项不同的显示名称属性,并且它们没有URL或子菜单集合本身。 Also, I think your JSON is more complicated than it needs to be: you don't need the intervening "MenuDetails" arrays, which always have exactly one element. 此外,我认为您的JSON比它需要的更复杂:您不需要介入的“MenuDetails”数组,它们总是只有一个元素。 Instead, I would suggest a simpler structure like this: 相反,我会建议一个更简单的结构:

[
    {
        "DisplayName" : "Top Menu 1",
        "Url" : "/Top1",
        "SubMenu" : 
        [
            {
                "DisplayName" : "SubMenu Item 1",
                "Url" : "/Top1/Sub1",
                "SubMenu" : 
                [
                   ...
                ]
            },
            {
                "DisplayName" : "SubMenu Item 2",
                "Url" : "/Top1/Sub2",
                "SubMenu" : 
                [
                   ...
                ]
            },
            ...
        ]
    },
    {
        "DisplayName" : "Top Menu 2",
        "Url" : "/Top2",
        "SubMenu" : 
        [
            ...
        ]
    },
    ...
]

Notice how the JSON is consistent at every level: each menu item has a DisplayName, a Url and a SubMenu, which is a (possibly empty) list of more menu items. 请注意JSON在每个级别的一致性:每个菜单项都有一个DisplayName,一个Url和一个SubMenu,它是一个(可能是空的)更多菜单项列表。 The consistency between levels is the key to a recursive structure. 级别之间的一致性是递归结构的关键。

To make this JSON, we first need a class to represent a menu item: 要制作这个JSON,我们首先需要一个类来表示一个菜单项:

class MenuItem
{
    public MenuItem()
    {
        SubMenu = new List<MenuItem>();
    }

    [JsonIgnore]
    public int Id { get; set; }
    [JsonIgnore]
    public int ParentId { get; set; }
    public string DisplayName { get; set; }
    public string Url { get; set; }
    public List<MenuItem> SubMenu { get; set; }
}

The next step is to translate your flat DataTable into a hierarchical structure. 下一步是将您的平面DataTable转换为分层结构。 To do this, I would first build a dictionary of MenuItems from the data table, keyed by Id : 为此,我首先从数据表构建一个MenuItems字典,用Id键入:

DataTable table = new DataTable();

table.Columns.Add("ResourceId", typeof(int));
table.Columns.Add("DisplayName", typeof(string));
table.Columns.Add("ParentId", typeof(int));
table.Columns.Add("Url", typeof(string));

table.Rows.Add(1, "Home", 0, "/Home");
table.Rows.Add(2, "Student", 0, "/Student");
table.Rows.Add(3, "Staff", 0, "/Staff");
table.Rows.Add(4, "Library", 0, "/Library");
table.Rows.Add(6, "StudentAtt", 2, "/Student/StudentAtt");
table.Rows.Add(7, "TimeTable", 1, "/Home/TimeTable");
table.Rows.Add(8, "Staff Att", 3, "/Staff/StaffAtt");
table.Rows.Add(9, "Book Issue", 4, "/Library/BookIssue");
table.Rows.Add(10, "Book Return", 4, "/Library/BookReturn");
table.Rows.Add(12, "Fee Payment", 11, "/Library/BookFine/FeePayment");
table.Rows.Add(11, "Book Fine", 4, "/Library/BookFine");

Dictionary<int, MenuItem> dict =
    table.Rows.Cast<DataRow>()
              .Select(r => new MenuItem
              {
                  Id = r.Field<int>("ResourceId"),
                  ParentId = r.Field<int>("ParentId"),
                  DisplayName = r.Field<string>("DisplayName"),
                  Url = r.Field<string>("Url")
              })
             .ToDictionary(m => m.Id);

Then loop through the dictionary, and for each menu item, look up its parent and add that item to the parent's children. 然后循环遍历字典,并为每个菜单项查找其父项并将该项添加到父项的子项中。 If an item has no parent (its ParentId is 0 ), instead add that item to a list of root menu items. 如果项目没有父项(其ParentId0 ),则将该项目添加到根菜单项列表中。 It only takes one pass through the dictionary to build the hierarchy in this way. 它只需要通过字典一次就能以这种方式构建层次结构。

List<MenuItem> rootMenu = new List<MenuItem>();

foreach (var kvp in dict)
{
    List<MenuItem> menu = rootMenu;
    MenuItem item = kvp.Value;
    if (item.ParentId > 0)
    {
        menu = dict[item.ParentId].SubMenu;
    }
    menu.Add(item);
}

Now that we have our hierarchy, it is trivial to serialize it using Json.Net. 既然我们已经拥有了层次结构,那么使用Json.Net对它进行序列化是微不足道的。 (Note the [JsonIgnore] attributes in the MenuItem class prevent the Id and ParentId values from being added to the JSON.) (注意, MenuItem类中的[JsonIgnore]属性会阻止将IdParentId值添加到JSON中。)

string json = JsonConvert.SerializeObject(rootMenu, Formatting.Indented);
Console.WriteLine(json);

Here is the final JSON produced by the above code: 这是上面代码生成的最终JSON:

[
  {
    "DisplayName": "Home",
    "Url": "/Home",
    "SubMenu": [
      {
        "DisplayName": "TimeTable",
        "Url": "/Home/TimeTable",
        "SubMenu": []
      }
    ]
  },
  {
    "DisplayName": "Student",
    "Url": "/Student",
    "SubMenu": [
      {
        "DisplayName": "StudentAtt",
        "Url": "/Student/StudentAtt",
        "SubMenu": []
      }
    ]
  },
  {
    "DisplayName": "Staff",
    "Url": "/Staff",
    "SubMenu": [
      {
        "DisplayName": "Staff Att",
        "Url": "/Staff/StaffAtt",
        "SubMenu": []
      }
    ]
  },
  {
    "DisplayName": "Library",
    "Url": "/Library",
    "SubMenu": [
      {
        "DisplayName": "Book Issue",
        "Url": "/Library/BookIssue",
        "SubMenu": []
      },
      {
        "DisplayName": "Book Return",
        "Url": "/Library/BookReturn",
        "SubMenu": []
      },
      {
        "DisplayName": "Book Fine",
        "Url": "/Library/BookFine",
        "SubMenu": [
          {
            "DisplayName": "Fee Payment",
            "Url": "/Library/BookFine/FeePayment",
            "SubMenu": []
          }
        ]
      }
    ]
  }
]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM