我有一个如下的分层数据表,它生成一个菜单及其子菜单。主菜单的ParentId0。每个子菜单都有一个ParentId,该ResourceId引用表中另一行的ParentId

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


我需要将数据转换为JSON。下面是我尝试过的代码。我正在尝试检查子菜单的ResourceId是否等于主菜单的table。但是不显示子菜单。 (变量DataTableParentId。)

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


我得到的结果如下:

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


但是预期结果是:

[
    {
        "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 "
                    }
                ]
            }
        ]
    }
]


我还需要显示子子菜单(其ResourceId指向子菜单的)。

最佳答案

您在问题中发布的“预期” JSON并不是完全递归的结构,因为它在各个级别之间是不一致的:子菜单项的显示名称属性与顶部菜单项的属性不同,并且它们没有URL或子菜单集合本身。另外,我认为您的JSON比需要的复杂得多:您不需要中间的“ MenuDetails”数组,该数组始终只有一个元素。相反,我建议使用一个更简单的结构,如下所示:

[
    {
        "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" :
        [
            ...
        ]
    },
    ...
]


请注意,JSON在每个级别上如何保持一致:每个菜单项都有一个DisplayName,一个Url和一个SubMenu,这是更多菜单项的列表(可能为空)。级别之间的一致性是递归结构的关键。

要制作此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; }
}


下一步是将平面DataTable转换为层次结构。为此,我首先要从数据表中建立一个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);


然后循环浏览字典,对于每个菜单项,查找其父项并将该项添加到父项的子项。如果某项没有父项(其ParentId0),则应将该项目添加到根菜单项列表中。只需一次通过字典即可以这种方式构建层次结构。

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


现在我们有了层次结构,使用Json.Net对其进行序列化很简单。 (请注意,[JsonIgnore]类中的MenuItem属性可防止将IdParentId值添加到JSON。)

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


这是上述代码生成的最终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": []
          }
        ]
      }
    ]
  }
]

07-28 03:10
查看更多