【问题标题】:Grouping, dynamic key formation, pivitoing information in C# & generating json output [closed]C# 中的分组、动态密钥形成、透视信息和生成 json 输出 [关闭]
【发布时间】:2016-03-28 20:01:07
【问题描述】:

我正在从 SQL Server 获取一些平面格式的分层数据。这就是数据的样子。

Group       Name        Level   Parent       Value1      Value2   RowID
Global      Product1     1                     10            20     1
APAC        Product1     1                     80            90     2
EMEA        Product1     1                     50            70     3
Global      Product2     2      Product1       100          200     4
APAC        Product2     2      Product1       800          900     5
EMEA        Product2     2      Product1       500          700     6
Global      Product3     3      Product2       10            20     7
APAC        Product3     3      Product2       80            90     8
Global      Product4     4      Product3       110           120     9
APAC        Product4     4      Product3       810           190     10
EMEA        Product4     4      Product3       510           170     11
.....
.....   
Global      Product12     1                     10            20     100

我需要根据Name and Level 列对数据进行分组。然后我需要根据Group Column & Value1 & Value2 columns 的组合创建多个键。请参阅下面的JSON 输出以了解有关输出的更多信息。请记住组的数量可以更改。

[
   {
     Id: 1,
     Name: Product1,
     parentId: null,
     GlobalValue1: 10,
     GlobalValue2: 20,
     APACValue1: 80,
     APACValue2: 90,
     EMEAValue1: 50,
     EMEAValue2: 70
   },
  {
     Id: 2,
     Name: Product2,
     parentId: 1, 
     GlobalValue1: 100,
     GlobalValue2: 200,
     APACValue1: 800,
     APACValue2: 900,
     EMEAValue1: 500,
     EMEAValue2: 700
   },
   {
     Id: 3,
     Name: Product3,
     parentId: 2, 
     GlobalValue1: 10,
     GlobalValue2: 20,
     APACValue1: 80,
     APACValue2: 90,
     EMEAValue1: null,
     EMEAValue2: null
   },
   {
     Id: 4,
     Name: Product4,
     parentId: 3, 
     GlobalValue1: 110,
     GlobalValue2: 120,
     APACValue1: 810,
     APACValue2: 190,
     EMEAValue1: 510,
     EMEAValue2: 170
   }{
     Id: 122,
     Name: Product12,
     parentId: null3, 
     GlobalValue1: 10,
     GlobalValue2: 20,
     APACValue1: null,
     APACValue2: null,
     EMEAValue1: null,
     EMEAValue2: null
   }



]

我需要自动生成 ID 字段。该字段将用于建立父子关系。 I cannot rely onnamecolumn as it can repeat (can have same value) across different levels.。将有超过 1 个产品和每个级别。在我的示例中,我只是为每个级别展示一个产品,以保持示例简单。

【问题讨论】:

  • 听起来你想从你的表中创建一个字典然后序列化它。例如,请参阅this
  • @MattBurland:感谢您的快速回复。如果我错了,请纠正我。但这只是车轮上的一个齿轮。它告诉我如何序列化数据。但在我这样做之前,我需要弄清楚如何生成密钥和管理父子关系。如何对数据进行分组?如何管理空值。我有一些想法,但有太多动人的部分,我有点困惑。
  • 您的问题并不完全清楚这些关系应该如何运作,但我认为您希望按Level 分组(看起来像LevelName 基本上是一对一的),然后对于每个组中的每个项目,创建一个字典并连接Group 和列名(Value1Value2)以制作字典键并粘贴相关值。然后将字典放入数组中并序列化整个内容。目前尚不清楚输出中的Id 来自何处。那只是Level吗?
  • 只需从您的输入中创建一个数组(linq 将是最好的方法),然后对其进行序列化。
  • @MattBurland:我不确定您所说的level and name are basically 1to1 是什么意思。我正在按名称和级别列对数据进行分组。由于名称字段可以跨级别重复,我不能依靠它来展示父子关系,这就是我想要创建的 ID 字段。

标签: c# .net json linq


【解决方案1】:

这里有一些代码可以满足我的需求。这是一个控制台应用程序,我使用 Newtonsoft.Json(在引用中使用 NuGet 获取它)来创建 JSON。

class Program
{
    static void Main(string[] args)
    {
        var data = initData();
        var result = getGroupedData(data);
        var json = new Newtonsoft.Json.JsonSerializer
        {
            Formatting=Formatting.Indented
        };
        json.Serialize(Console.Out,result);
        Console.ReadKey();
    }

    private static object getGroupedData(DataTable dt)
    {
        var id = 1;
        var rows = dt.Rows.OfType<DataRow>(); //because for some reason DataRowCollection is not an IEnumerable<DataRow>
        var products = rows //the products, each defined by Name and Level
          .GroupBy(r => new { Name = r.Field<string>("Name"), Level = r.Field<int>("Level") })
          .Select(g => new
          {
              Id = id++, //create the id
              Name = g.Key.Name,
              Level = g.Key.Level,
              // select the parent and throw exception if there are more or less than one
              Parent = g.Select(r => r.Field<string>("Parent")).Distinct().Single() 
          }).ToList();
        var results = products
          .Select(p => new //need a partial result first, containing the Global, Apac and Emea rows, if they exist
          {
              Id = p.Id,
              Name = p.Name,
              // Assuming the Level of a child is Level of parent+1
              Parent = products.FirstOrDefault(par => par.Name == p.Parent && par.Level + 1 == p.Level),
              Global = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "Global"),
              Apac = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "APAC"),
              Emea = rows.FirstOrDefault(r => r.Field<string>("Name") == p.Name && r.Field<int>("Level") == p.Level && r.Field<string>("Group") == "EMEA")
          })
          .Select(x => new //create the final result
          {
              Id = x.Id,
              Name = x.Name,
              ParentId = x.Parent==null? (int?)null :x.Parent.Id,
              GlobalValue1 = x.Global == null ? (double?)null : x.Global.Field<double?>("Value1"),
              GlobalValue2 = x.Global == null ? (double?)null : x.Global.Field<double?>("Value2"),
              APACValue1 = x.Apac == null ? (double?)null : x.Apac.Field<double?>("Value1"),
              APACValue2 = x.Apac == null ? (double?)null : x.Apac.Field<double?>("Value2"),
              EMEAValue1 = x.Emea == null ? (double?)null : x.Emea.Field<double?>("Value1"),
              EMEAValue2 = x.Emea == null ? (double?)null : x.Emea.Field<double?>("Value2")
          })
          .ToArray();
        return results;
    }

    private static DataTable initData()
    {
        var dt = new DataTable();
        dt.Columns.Add("Group", typeof(string));
        dt.Columns.Add("Name", typeof(string));
        dt.Columns.Add("Level", typeof(int));
        dt.Columns.Add("Parent", typeof(string));
        dt.Columns.Add("Value1", typeof(double));
        dt.Columns.Add("Value2", typeof(double));
        dt.Columns.Add("RowID", typeof(int));
        dt.Rows.Add("Global", "Product1", 1, null, 10, 20, 1);
        dt.Rows.Add("APAC", "Product1", 1, null, 80, 90, 2);
        dt.Rows.Add("EMEA", "Product1", 1, null, 50, 70, 3);
        dt.Rows.Add("Global", "Product2", 2, "Product1", 100, 200, 1);
        dt.Rows.Add("APAC", "Product2", 2, "Product1", 800, 900, 2);
        dt.Rows.Add("EMEA", "Product2", 2, "Product1", 500, 700, 3);
        dt.Rows.Add("Global", "Product3", 3, "Product2", 10, 20, 1);
        dt.Rows.Add("APAC", "Product3", 3, "Product2", 80, 90, 2);
        dt.Rows.Add("Global", "Product4", 4, "Product3", 110, 120, 1);
        dt.Rows.Add("APAC", "Product4", 4, "Product3", 810, 190, 2);
        dt.Rows.Add("EMEA", "Product4", 4, "Product3", 510, 170, 3);
        return dt;
    }
}

使用 POCO 对象,方法如下所示:

private static object getGroupedData(IEnumerable<MyPoco> rows)
{
    var id = 1;
    var products = rows //the products, each defined by Name and Level
      .GroupBy(r => new { Name = r.Name, Level = r.Level })
      .Select(g => new
      {
          Id = id++, //create the id
          Name = g.Key.Name,
          Level = g.Key.Level,
          // select the parent and throw exception if there are more or less than one
          Parent = g.Select(r => r.Parent).Distinct().Single() 
      }).ToList();
    var results = products
      .Select(p => new //need a partial result first, containing the Global, Apac and Emea rows, if they exist
      {
          Id = p.Id,
          Name = p.Name,
          // Assuming the Level of a child is Level of parent+1
          Parent = products.FirstOrDefault(par => par.Name == p.Parent && par.Level + 1 == p.Level),
          Global = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "Global"),
          Apac = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "APAC"),
          Emea = rows.FirstOrDefault(r => r.Name == p.Name && r.Level == p.Level && r.Group == "EMEA")
      })
      .Select(x => new //create the final result
      {
          Id = x.Id,
          Name = x.Name,
          ParentId = x.Parent==null? (int?)null :x.Parent.Id,
          GlobalValue1 = x.Global == null ? (double?)null : x.Global.Value1,
          GlobalValue2 = x.Global == null ? (double?)null : x.Global.Value2,
          APACValue1 = x.Apac == null ? (double?)null : x.Apac.Value1,
          APACValue2 = x.Apac == null ? (double?)null : x.Apac.Value2,
          EMEAValue1 = x.Emea == null ? (double?)null : x.Emea.Value1,
          EMEAValue2 = x.Emea == null ? (double?)null : x.Emea.Value2
      })
      .ToArray();
    return results;
}

【讨论】:

  • 为您的请求更新了它,使其具有名称+级别的唯一性,而不仅仅是名称
  • 感谢您的快速回复和多次编辑。我假设在这种情况下 dt 是一个数据表。这条线在做什么ParentId=products.FirstOrDefault(par=&gt;par.Name==p.Parent&amp;&amp;par.Level+1==p.Level)?.Id, 。我无法编译此代码。你能解释一下你在做什么,我会让代码兼容。
  • 如果起始数据不是 DataTable 会容易得多。由于您没有指定它,我假设“平面 SQL 数据”是指 DataTable 或 DataReader。让我做一个简短的项目,并为您提供可编译的注释正确代码。
  • 使用控制台应用更新代码,返回您想要的结果。
  • 谢谢你的回答!!对此,我真的非常感激。我可以使用普通的 C# 类来存储数据,而不是使用数据表。此外,我无法编译您共享的代码(因为 C# 6.0 功能......不确定是什么问题)。您能否再分享一个使用 C# 类作为数据源并避免使用 C# 6 功能的示例。我不能感谢你!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多