.net 如何将层次数据从DataTable转换为JSON

.net 如何将层次数据从DataTable转换为JSON,.net,json,asp.net-mvc,linq,json.net,.net,Json,Asp.net Mvc,Linq,Json.net,我有一个分层数据表,如下所示,它生成一个菜单及其子菜单。主菜单的父ID为0。每个子菜单都有一个引用表中另一行的ResourceId的ParentId ResourceId DisplayName ParentId Url ----------------------------------------------- 1 Home 0 Some Url 2 Student 0

我有一个分层数据表,如下所示,它生成一个菜单及其子菜单。主菜单的父ID为
0
。每个子菜单都有一个引用表中另一行的
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

但预期结果是:

[
    {
        "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和一个子菜单,这是一个(可能是空的)更多菜单项的列表。级别之间的一致性是递归结构的关键

要制作此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; }
}
然后循环浏览字典,对于每个菜单项,查找其父项并将该项添加到父项的子项中。如果某项没有父项(其父项ID为
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:

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

也许这会有所帮助:@user1925921您的预期结果实际上不是递归结构,也不包含表中的所有信息(值得注意的是,子菜单中缺少URL,并且没有子菜单来处理“Book Fine”)。那么,你是真的在寻找你发布的结果,还是你真正的目标是得到一个支持任意级别菜单和子菜单的完全递归结构?我需要一个完全递归的结构
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);
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);
}
string json = JsonConvert.SerializeObject(rootMenu, Formatting.Indented);
Console.WriteLine(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": []
          }
        ]
      }
    ]
  }
]