2016-03-28 4 views
0

Ich erhalte einige hierarchische Daten im flachen Format vom SQL-Server. So sehen die Daten aus.Gruppierung, dynamische Schlüsselbildung, Pivot-Informationen in C# und Generierung von JSON-Ausgabe

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 

ich zu einer Gruppe müssen die auf Name and Level Spalten basierten Daten. Dann muss ich mehrere Schlüssel basierend auf der Kombination Group Column & Value1 & Value2 columns erstellen. Sehen Sie sich die Ausgabe JSON an, um mehr über die Ausgabe zu erfahren. Bitte beachten Sie, dass sich die Nummer der Gruppe ändern kann.

[ 
    { 
    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 
    } 



] 

Ich muss ID-Feld automatisch generieren. Dieses Feld wird verwendet, um eine Eltern-Kind-Beziehung herzustellen. I cannot rely on Name column as it can repeat (can have same value) across different levels.. Es wird mehr als 1 Produkt und jedes Level geben. In meinem Beispiel zeige ich nur ein Produkt pro Level, um das Beispiel einfach zu halten.

+0

Klingt wie Sie ein Wörterbuch aus Ihrer Tabelle erstellen möchten, und dann die Serialisierung. Siehe zum Beispiel [http://www.newtonsoft.com/json/help/html/serializedictionary.htm]. –

+0

@MattBurland: Danke für die schnelle Antwort. Bitte korrigieren Sie mich, wenn ich falsch liege. Aber das ist nur eine Kippe im Rad. Es sagt mir, wie man die Daten serialisiert. Aber bevor ich das mache, muss ich herausfinden, wie man Schlüssel generiert und die Eltern-Kind-Beziehung verwaltet. Wie gruppiere ich die Daten? Wie verwalte ich Nullwerte? Ich habe bestimmte Ideen, aber es gibt zu viele bewegliche Teile und ich bin etwas verwirrt. – OpenStack

+0

Ihre Frage ist nicht ganz klar, wie die Beziehungen funktionieren sollen, aber * ich denke * Sie suchen nach "Level" zu gruppieren (es sieht aus wie "Level" und "Name" sind im Grunde 1: 1) Erstellen Sie dann für jedes Element in jeder Gruppe ein Dictionary und verketten Sie 'Group' und die Spaltennamen (' Value1', 'Value2'), um Dictionary-Schlüssel zu erstellen, und kleben Sie die relevanten Werte hinein. Dann kleben Sie das Dictionary in ein Array und serialisieren das ganze Ding. Es ist nicht klar, woher Ihre ID in der Ausgabe kommt. Ist das nur "Level"? –

Antwort

1

Hier ist ein bisschen Code, der tut, was ich denke, dass Sie wollen. Es ist eine Konsolenanwendung und ich habe Newtonsoft.Json verwendet (um es mit NuGet in Referenzen zu bekommen), um JSON zu erstellen.

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; 
    } 
} 

Mit POCO Objekt, würde das Verfahren wie folgt aussehen:

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; 
} 
+0

Aktualisiert für Ihre Anfrage Name + Level Unicity, nicht nur Name –

+0

Vielen Dank für die schnelle Antwort und mehrere Änderungen. Ich nehme an, dass in diesem Fall 'dt' eine Datentabelle ist. Was macht diese Zeile? ParentId = products.FirstOrDefault (par => par.Name == p.Parent && par.Level + 1 == p.Level) ?. Id, '. Ich kann diesen Code nicht kompilieren. Kannst du mir bitte erklären, was du machst und ich werde Code komplibar machen. – OpenStack

+0

Es wäre viel einfacher, wenn die Startdaten keine DataTable wären. Wie Sie es nicht angegeben haben, nahm ich an, dass flache SQL-Daten entweder eine DataTable oder einen DataReader bedeuten. Lass mich ein kurzes Projekt machen und gebe dir den kompilierbaren kommentierten korrekten Code. –

Verwandte Themen