【问题标题】:Grouping flat data to create a hierarchical tree using LINQ for JSON使用 LINQ for JSON 对平面数据进行分组以创建分层树
【发布时间】:2021-09-14 23:17:30
【问题描述】:

我正在尝试将平面数据源投影到可以使用 Newtonsoft.Json 直接序列化为 JSON 的对象中。我在 Linqpad 中创建了一个小程序,其中包含一个想象的库存概览作为测试。请求的输出如下:

  • 网站名称
    • 库存名称
      • 产品名称
      • 重量
      • 单位
    • 库存名称
      • 产品(等)

对于我的生活,我不能让它只有一个“站点名称”作为唯一的根对象。我想列出网站内库存中的内容,但它总是最终看起来像:

如何使“网站”与众不同,拥有一组“库存”,每个库存都有一组“产品”?

我的实际数据源是一个数据库表,它类似于我的测试对象的结构 - 它就是这样。

Linqpad中的测试代码:(注意引用Newtonsoft.Json)

void Main()
{
    var contents = new List<DatabaseRecord>()
    {
        new DatabaseRecord{Product="Autoblaster", Inventory="Hull 5", Site="Death star", Units=20,Weight=500},
        new DatabaseRecord{Product="E11 Blaster Rifle", Inventory="Hull 5", Site="Death star", Units=512,Weight=4096},
        new DatabaseRecord{Product="SWE/2 Sonic Rifle", Inventory="Hull 1", Site="Death star", Units=20,Weight=500},
        new DatabaseRecord{Product="Relby v10 Micro Grenade Launcher", Inventory="Hull 5", Site="Death star", Units=20,Weight=500},
        new DatabaseRecord{Product="T-8 Disruptor", Inventory="Hull 1", Site="Death star", Units=20,Weight=500},
        new DatabaseRecord{Product="E11 Blaster Rifle", Inventory="Hull 2", Site="Death star", Units=50,Weight=1200}
    };
    
    var inventorycontent = from row in contents
                    group row by row.Site into sites
                    orderby sites.Key
                    select from inventory in sites
                        group inventory by inventory.Inventory into inventories
                           orderby inventories.Key
                           select new
                           {
                               site = sites.Key,
                               inventory = inventories.Key,
                               lines = inventories.Select(i => new { i.Product, i.Weight, i.Units })
                           };

    contents.Dump();
    inventorycontent.Dump();
    
    JsonConvert.SerializeObject(inventorycontent, Newtonsoft.Json.Formatting.Indented).Dump();
}

// Define other methods and classes here
class DatabaseRecord
{
    public string Product { get; set; }
    public string Inventory { get; set; }
    public string Site { get; set; }
    public int Units { get; set; }
    public double Weight { get; set; }
    
}

JSON 输出:

[
  [
    {
      "site": "Death star",
      "inventory": "Hull 1",
      "lines": [
        {
          "Product": "SWE/2 Sonic Rifle",
          "Weight": 500.0,
          "Units": 20
        },
        {
          "Product": "T-8 Disruptor",
          "Weight": 500.0,
          "Units": 20
        }
      ]
    },
    {
      "site": "Death star",
      "inventory": "Hull 2",
      "lines": [
        {
          "Product": "E11 Blaster Rifle",
          "Weight": 1200.0,
          "Units": 50
        }
      ]
    },
    {
      "site": "Death star",
      "inventory": "Hull 5",
      "lines": [
        {
          "Product": "Autoblaster",
          "Weight": 500.0,
          "Units": 20
        },
        {
          "Product": "E11 Blaster Rifle",
          "Weight": 4096.0,
          "Units": 512
        },
        {
          "Product": "Relby v10 Micro Grenade Launcher",
          "Weight": 500.0,
          "Units": 20
        }
      ]
    }
  ]
]

建议的正确输出样本:

{
  "sites":[{
    "site": "Death star",
    "inventories":[
      {
        "name":"Hull 1",
        "lines":[{
          "Product": "SWE/2 Sonic Rifle",
          "Weight": 500.0,
          "Units": 20
        },
        {
          "Product": "T-8 Disruptor",
          "Weight": 500.0,
          "Units": 20
        }]
      },
      {
        "name":"Hull 2",
        "lines":[{
          "Product": "SWE/2 Sonic Rifle",
          "Weight": 500.0,
          "Units": 20
        }]
      }
      ]
    },
    {"site": "Other site",
    "inventories":[
      {
        "name":"Hull 1",
        "lines":[{
          "Product": "SWE/2 Sonic Rifle",
          "Weight": 500.0,
          "Units": 20
        }]
      }]
    }]
}

【问题讨论】:

  • 不使用匿名类型和分组...我会使用分组和字典。
  • 你能展示一个正确的 JSON 输出样本吗?
  • @NetMage 添加了示例 JSON 输出
  • 我稍微更改了答案以匹配您的 json 输出

标签: c# linq


【解决方案1】:

好的,我有一个使用字典的解决方案,可以将所有内容正确分组:

           //first get everything properly grouped with dictionaries
           var result = contents
            .GroupBy(x => x.Site)
            .ToDictionary(g => g.Key, g => g
                                .GroupBy(i => i.Inventory)
                                .ToDictionary(i => i.Key, i => i
                                        .Select(a => new 
                                            { 
                                                Product = a.Product, 
                                                Weight = a.Weight, 
                                                Units = a.Units 
                                            })
                                            .ToList()));

            //project to a new object that matches your desired json  
            var formattedResult = new
            {
                sites = (from r in result
                         select new
                         {
                             site = r.Key,
                             inventories = (from i in r.Value select new { name = i.Key, lines = i.Value }).ToList()
                         }).ToList()
            };

这是输出的json:

{
   "sites": [
    {
      "site": "Death star",
      "inventories": [
        {
          "name": "Hull 5",
          "lines": [
            {
              "Product": "Autoblaster",
              "Weight": 500.0,
              "Units": 20
            },
            {
              "Product": "E11 Blaster Rifle",
              "Weight": 4096.0,
              "Units": 512
            },
            {
              "Product": "Relby v10 Micro Grenade Launcher",
              "Weight": 500.0,
              "Units": 20
            }
          ]
        },
        {
          "name": "Hull 1",
          "lines": [
            {
              "Product": "SWE/2 Sonic Rifle",
              "Weight": 500.0,
              "Units": 20
            },
            {
              "Product": "T-8 Disruptor",
              "Weight": 500.0,
              "Units": 20
            }
          ]
        },
        {
          "name": "Hull 2",
          "lines": [
            {
              "Product": "E11 Blaster Rifle",
              "Weight": 1200.0,
              "Units": 50
            }
          ]
        }
      ]
    }
  ]
}

如您所见,我按站点分组,然后将其放入字典中,然后按库存分组,并将其放入另一个字典,其中包含产品列表作为值。

所以基本上结果是一个

 Dictionary<string, Dictionary<string, List<Products>>>

字典键是站点、库存名称。

每次我必须像这样构建分层 json 时,我都会使用这个 GroupBy -->> 字典。

【讨论】:

  • 非常感谢!这是一个很好的解决方案,我从未考虑过为此使用字典。但是,如果您想显式命名这些值怎么办?所以,而不是 "Death star":{(...)} 你想要: "Site": {"Name": "Death star", "Inventories" :[ (...) ] 等。换句话说,与我的问题中的输出更密切相关?此外,您的示例中有一个小错误:最后一个 Select 使用 i,但该名称已在该范围内使用。我将其更改为 a 并且一切正常。
  • @GeirR 我添加了一小段代码和更新的 json 输入。我只是将字典投影到与您想要的 json 匹配的对象中。
猜你喜欢
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多