[英]How to convert hierarchical data from a DataTable to JSON
我有一個如下的分層數據表,它生成一個菜單及其子菜單。 主菜單的ParentId
為0
。 每個子菜單都有一個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
我需要將數據轉換為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": []
}
]
}
]
}
]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.