我有一个分层数据表,如下所示,它生成一个菜单及其子菜单。主菜单具有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。下面是我试用过的代码。我正在尝试检查子菜单的ParentId
是否等于主菜单的ResourceId
。但不显示子菜单。(变量table
是DataTable
。)
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 "
}
]
}
]
}
]
我还需要显示子菜单(其ParentId
指向子菜单的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对其进行序列化是很简单的。(请注意,MenuItem
类中的[JsonIgnore]
属性阻止将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": []
}
]
}
]
}
]