如何在 C# 中创建嵌套(父子)JSON 响应?

Posted

技术标签:

【中文标题】如何在 C# 中创建嵌套(父子)JSON 响应?【英文标题】:How to create a nested (parent-Child ) JSON response in c#? 【发布时间】:2019-03-22 14:07:53 【问题描述】:

我正在制作一个 Web 服务,它将以 JSON 格式给出响应。我已经从 sql server 获取数据并将其存储在 Datatable 中。这是 dt 的样子:-

id      Caption                 pid
F182    GLOBAL REPORTS          NULL
F184    software                NULL
F1227   LYB P&L Reports         F184
F1245   LYB Training            F184
F1239   test3                   F182
F1249   Paavan_Test_Reports     F184

标题列中 pid 为 Null 的项目是父母,他们的孩子与各自父母的 id 具有相同的 pid .

例如: GLOBAL REPORTS 有 1 个孩子,即 test3software 有 3 个孩子。

我希望 JSON 响应为以下格式

[ 
    id='F182',  
    caption='GLOBAL REPORTS',
    pid=null;
    items:[
    id='F1239',
    caption='test3',
    pid='F182'] 
    ,
     
    id='F184',
    caption='software',
    pid='NULL',
    items:[
    id='F1227',
    caption='LYB P&L Reports',
    pid='F184'
    ,
    
    id='F1245',
    caption='LYB Training',
    pid='F184'
    ,
     
    id='F1249',
    caption='Paavan_Test_Reports',
    pid='F184'
    
]

我已经创建了一个类文件夹

    class folder
    
    string idget;set;
    string pidget;set;
    string captionget;set;
   

我如何获得对象数组items,其中包含特定父项的所有可用子项?我是 Json 对象和响应的新手,

我尝试过使用以下方法:

 var obj = dt.AsEnumerable()
                .GroupBy(r => r["pid"])
                .ToDictionary(g => g.Key.ToString(),
                              g => g.Select(r => new 
                              item = r["caption"].ToString(),
                              ).ToArray());
     var json = JsonConvert.SerializeObject(obj);

但这给了我一个没有任何层次结构的简单 json 响应。

【问题讨论】:

【参考方案1】:

您需要在序列化之前构建对象层次结构。为此,请在 folder 类中定义一个新属性:

public IEnumerable<folder> items  get; set; 

现在您可以递归搜索每个根 folder 的子节点:

public static IEnumerable<folder> BuildTree(folder current, folder[] allItems)

    var childs = allItems.Where(c => c.pid == current.id).ToArray();
    foreach (var child in childs)
        child.items = BuildTree(child, allItems);
    current.items = childs;
    return childs;

用法:

var input = new[] // dt.AsEnumerable() in your case

    new folder id = "F182",  caption = "GLOBAL REPORTS",      pid = null   ,
    new folder id = "F184",  caption = "software",            pid = null   ,
    new folder id = "F1227", caption = "LYB P&L Reports",     pid = "F184" ,
    new folder id = "F1245", caption = "LYB Training",        pid = "F184" ,
    new folder id = "F1239", caption = "test3",               pid = "F182" ,
    new folder id = "F1249", caption = "Paavan_Test_Reports", pid = "F184" ,
;

var roots = input.Where(i => i.pid == null);
foreach (var root in roots)
    BuildTree(root, input);

var json = JsonConvert.SerializeObject(roots, Formatting.Indented);

另外,如果你想隐藏空的items,你可以在folder类中定义方法ShouldSerialize

public bool ShouldSerializeitems()

    return items.Any();

演示是here

【讨论】:

【参考方案2】:

我认为你想要的是在文件夹中有一个文件夹列表,如下所示:

class folder

    string idget;set;
    string pidget;set;
    string captionget;set;
    List<folder> items get;set;

在这种情况下使用 JSONConvert 时,如果子集合中存在父对象,则可能会遇到循环依赖。您可以使用 SerializerSettings 对象来忽略这些情况,对于项目列表中的子项,您可以将其集合设置为 null,这样它就不会被序列化。

但是,您指定的所需输出还列出了一组文件夹。所以最后,您需要序列化文件夹类的集合(例如列表或数组)。类似的东西。

关于如何加载数据,我想您已经加载了带有 id、pid 和标题的文件夹,但 items 属性为空。

为了建立父/子关系,你可以这样

var folders = dt.AsEnumerable().ToList();

foreach(var folder in folders) 
    folder.items = folders.Where(f => f.pid.Equals(folder.id)).ToList();


var parentFolders = folders.Where(f => f.pid is null);

var json = JsonConvert.SerializeObject(parentFolders, new SerializerSettings () 
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    );

【讨论】:

这是一个硬编码的解决方案,如果数据发生变化,它就不再起作用了。 抱歉,您的意思是如何硬编码?上面的代码示例只是为了提供一个示例来说明您将如何使用它。 问题是如何从扁平化的数据中创建分层数据,然后序列化。你只解决了第二部分。 你是对的,对不起。我已经稍微改变了我的答案,希望它更充实。假设这里引用循环处理是无关紧要的,如果仍然存在父/子关系。 Ajay 如果这能解决您的问题,您会将其标记为答案吗?

以上是关于如何在 C# 中创建嵌套(父子)JSON 响应?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中创建 JSON 字符串

在 .NET 中创建 C++ ATL COM 嵌套命名空间,如 System 命名空间

如何在Vapor 3中从JSON响应中保存父子关系

如何从 Vapor 3 中的 JSON 响应中保存父子关系

在 C# 中,如何为具有多个嵌套数组的 JSON 对象建模?

如何在BigQuery中创建嵌套字段和数组的数组