我有一个如下的分层数据表,它生成一个菜单及其子菜单。主菜单的ParentId
为0
。每个子菜单都有一个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
。但是不显示子菜单。 (变量DataTable
是ParentId
。) 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);
然后循环浏览字典,对于每个菜单项,查找其父项并将该项添加到父项的子项。如果某项没有父项(其
ParentId
为0
),则应将该项目添加到根菜单项列表中。只需一次通过字典即可以这种方式构建层次结构。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
属性可防止将Id
和ParentId
值添加到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": []
}
]
}
]
}
]