2017-03-22 3 views
0

Wir arbeiten an einem Programm, um Slide-Image-Daten von einer Gruppe von Servern zu holen, die kein konsistentes Schema-Setup haben (ich mache mir Sorgen, dass es ungültig ist, aber ich bin nicht kompetent genug, um diesen Anruf zu machen). Wir haben keinen Einfluss auf die Server als unabhängige unabhängige Forscher.Effiziente Deserialisierung dynamischer JSON-Daten in Datentabelle

Die Daten wurden größtenteils (größtenteils) über eine große Anzahl von Formularen (n> 50) mit inkonsistenten Feldern (Daten gehen in die 90er Jahre zurück) eingegeben. Hier ist ein Beispiel für eine Antwort:

{ 
"form12873": [ 

    { 
     "id": "9202075838", 
     "timestamp": "2015-06-25 10:24:51", 
     "user_agent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit\/600.6.3 (KHTML, like Gecko) Version\/8.0.6 Safari\/600.6.3", 
     "remote_addr": "[Re.dact.ed]", 
     "processed": "1", 
     "data": { 
      "33885124": { 
       "field": "33885124", 
       "value": "CDat Lab", 
       "flat_value": "CDat Lab", 
       "label": "Completed by:", 
       "type": "select" 
      }, 

      ''**Several more fields as above**''... 

      "33884660": { 
       "field": "33884660", 
       "value": { 
        "slideX": "2456123", 
        "slideY": "456632", 
        "label": "K-20150322148", 
        "approved": "1", 
        "score": "30144" 
       }, 
       "flat_value": "slideX = 2456123\nslideY = 456632\nlabel = K-20150322148\napproved = 1\nscore = 30144", 
       "label": "Slide Stats:", 
       "type": "slidestats" 
      }, 

      ''**Some of the fields are as above... 

      "31970564": { 
       "field": "31970564", 
       "value": [ 
        "System", 
        "Crated", 
        "Mirax", 
        "NanoZoomer", 
        "ThinPrep", 
        "Aperio", 
        "Intellisite" 

       ], 
       "flat_value": "System\nCrated\nMirax\nNanoZoomer\nThinPrep\nAperio\nIntellisite", 
       "label": "System Information", 
       "type": "checkbox" 
      }, 

      ''**Some of the values are Arrays... 

      "33883781": { 
       "field": "33883781", 
       "selection": "Retain", 
       "label": "4. Retain\/Remove\/Review", 
       "type": "selectdrop" 
      }, 

      ''**Some of the fields don't have the same children 

      "52792890": { 
       "field": "52792890", 
       "image": "'A really large byte[], removed for ease of reading'", 
       "type": "image" 
      } 

      ''**Somewhere near the end of each response is the actual image... 
     } 
    }, 

    { 
     "id": "33884681", 
      ''**Then it continues on as above until the end: 
    } 
], "total": 170, "pages": 5, "pretty_id": "478125624983" } 

In der Vergangenheit, als ich habe zu model/class for the structure of the JSON der Lage gewesen, habe ich gewusst, wie sie damit umgehen (machen Sie eine Datenklasse mit Feld, Wert, usw. definiert).

Der Versuch, Lösungen wie:

var result = JsonConvert.DeserializeObject<List<Dictionary<string, 
          Dictionary<string, string>>>>(content); 

immer führte zu Array Fehler oder Besetzungsausgaben (auch wenn direkte Abgüsse wurden hinzugefügt). Ich bin in der Lage die tatsächlichen first array using zu bekommen:

Public Shared Function Tabulate(json As String) As DataTable 
    Dim jsonLinq = Newtonsoft.Json.Linq.JObject.Parse(json) 

    ' Find the first array using Linq 

    Dim srcArray = jsonLinq.Descendants().Where(Function(d) TypeOf d Is JArray).First() 
    Dim trgArray = New Newtonsoft.Json.Linq.JArray() 
    For Each row As JObject In srcArray.Children(Of JObject)() 
     Dim cleanRow = New JObject() 
     For Each column As JProperty In row.Properties() 
      ' Only include JValue types 
      If TypeOf column.Value Is JValue Then 
       cleanRow.Add(column.Name, column.Value) 
      End If 
     Next 

     trgArray.Add(cleanRow) 
    Next 


    Return JsonConvert.DeserializeObject(Of DataTable)(trgArray.ToString()) 
End Function 

Mein Endziel auch zu einer Datentabelle zu erhalten ist, und Looping/das Bild Byte hat ich besorgt über versucht, regressiv zu gehen, um weitere Kinder. Meine Versuche, dann mit dem ersten Array zu deserialisieren, sind dann auf Null gekommen.

Wenn es einen schnellen Weg gibt, damit umzugehen, würde ich die Lösung lieben. Wenn das Problem ist, dass ich versuche, mit JSON zu umgehen, würde ich gerne einen Hinweis darauf bekommen, wo der aktuelle Standard gebrochen wird (so kann ich zumindest versuchen, die andere Institution dazu zu bringen, ihre Server zu wechseln). Das heißt, ich werde mich wahrscheinlich sowieso damit beschäftigen müssen, auch wenn es Loops sind.

* Hinweis: Das Projekt wurde in VB.net gestartet, also haben wir es so gehalten, aber ich kann trotzdem entscheiden, auf C# zu portieren. Code in beiden wäre großartig.

Unten ist ein nicht markiertes Beispiel des Json, das zum Testen verwendet werden kann. Mein Endziel ist diese in eine Datentabelle zu glätten:

{ 
"form12873": [ 
    { 
     "id": "9202075838", 
     "timestamp": "2015-06-25 10:24:51", 
     "user_agent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit\/600.6.3 (KHTML, like Gecko) Version\/8.0.6 Safari\/600.6.3", 
     "remote_addr": "[Re.dact.ed]", 
     "processed": "1", 
     "data": { 
      "33885124": { 
       "field": "33885124", 
       "value": "CDat Lab", 
       "flat_value": "CDat Lab", 
       "label": "Completed by:", 
       "type": "select" 
      }, 
      "33884660": { 
       "field": "33884660", 
       "value": { 
        "slideX": "2456123", 
        "slideY": "456632", 
        "label": "K-20150322148", 
        "approved": "1", 
        "score": "30144" 
       }, 
       "flat_value": "slideX = 2456123\nslideY = 456632\nlabel = K-20150322148\napproved = 1\nscore = 30144", 
       "label": "Slide Stats:", 
       "type": "slidestats" 
      }, 
      "31970564": { 
       "field": "31970564", 
       "value": [ 
        "System", 
        "Crated", 
        "Mirax", 
        "NanoZoomer", 
        "ThinPrep", 
        "Aperio", 
        "Intellisite" 
       ], 
       "flat_value": "System\nCrated\nMirax\nNanoZoomer\nThinPrep\nAperio\nIntellisite", 
       "label": "System Information", 
       "type": "checkbox" 
      }, 



      "33883781": { 
       "field": "33883781", 
       "selection": "Retain", 
       "label": "4. Retain\/Remove\/Review", 
       "type": "select" 
      } 
     } 
    } 
], "total": 170, "pages": 5, "pretty_id": "478125624983" } 
+0

vielleicht wird die akzeptierte Antwort hier helfen? http://stackoverflow.com/questions/947241/how-do-i-create-dynamic-properties-in-c – Muckeypuck

+0

@Muckeypuck Würde das nicht nur funktionieren, wenn alle Kinder unter jedem Knoten einheitlich sind? IE die Elemente unter "Daten", die nicht die gleiche Anzahl/Arten von Eigenschaften hatten? Ich habe versucht, dynamische Eigenschaften zu implementieren, aber bis jetzt, als ich diese verknüpfte Lösung ausprobierte, bin ich bei der Deserialisierung immer noch gescheitert. Das mag an meinem mangelnden Verständnis liegen, also werde ich es weiter versuchen. –

+3

Nach mehreren Ansätzen war die beste Lösung für mich, aus schlechten/unvorhersehbaren JSON-Daten zu lesen, es in ein 'JToken'-Objekt zu parsen und' .SelectTokens' und JSONPath zu verwenden, um herauszufinden, was ich brauchte oder ohne es zu entdecken mein Code stürzt dazwischen. Ist das eine Option für dich? – VBobCat

Antwort

1

Die hässliche Vorrichtung unten kann (grob) was Sie wollen. Geben Sie Ihre json-Quellenzeichenfolge als Parameter an DeserializeToDataTable und sammeln Sie die Ergebnisdatentabelle. Es hat an deiner Probe funktioniert. Ich kann nicht garantieren, dass dies für den Rest Ihrer Daten ausreicht. Der Zweck hier ist es, ein funktionierendes Start-Kit zur Verfügung zu stellen, das Sie studieren, verstehen, debuggen und an Ihre Bedürfnisse anpassen können.

Private Function DeserializeToDataTable(ByVal jsource As String) 
    Dim JRootObject = JObject.Parse(jsource) 
    Dim Children = JRootObject.SelectTokens("$..data.*").ToArray 
    Dim Records = Children.OfType(Of JObject).ToArray 
    Dim dicList As New List(Of Dictionary(Of String, Object)) 
    For Each rec In Records 
     dicList.Add(DeserializeToDictionary(rec)) 
    Next 
    Dim fieldnames = dicList.SelectMany(Function(d) d.Keys).Distinct.ToArray 
    Dim dt As New DataTable 
    For Each fieldname In fieldnames 
     dt.Columns.Add(fieldname, GetType(Object)) 
    Next 
    Dim row As DataRow 
    For Each dic In dicList 
     row = dt.NewRow 
     For Each kvp In dic 
      row.SetField(kvp.Key, kvp.Value) 
     Next 
     dt.Rows.Add(row) 
    Next 
    Return dt 
End Function 

Private Function DeserializeToDictionary(ByVal json_object As JObject) As Dictionary(Of String, Object) 
    Dim dic = New Dictionary(Of String, Object) 
    For Each field In json_object.Properties 
     Select Case field.Value.Type 
      Case JTokenType.Array 
       Dim subobject = New JObject 
       Dim item = 0 
       For Each token In field.Value 
        subobject("item" & item) = token 
        item += 1 
       Next 
       Dim subdic = DeserializeToDictionary(subobject) 
       For Each kvp In subdic 
        dic(kvp.Key) = kvp.Value 
       Next 
      Case JTokenType.Boolean 
       dic(field.Name) = field.Value.ToObject(Of Boolean) 
      Case JTokenType.Bytes 
       dic(field.Name) = field.Value.ToObject(Of Byte()) 
      Case JTokenType.Date 
       dic(field.Name) = field.Value.ToObject(Of Date) 
      Case JTokenType.Float 
       dic(field.Name) = field.Value.ToObject(Of Double) 
      Case JTokenType.Guid 
       dic(field.Name) = field.Value.ToObject(Of Guid) 
      Case JTokenType.Integer 
       dic(field.Name) = field.Value.ToObject(Of Integer) 
      Case JTokenType.Object 
       Dim subdic = DeserializeToDictionary(field.Value) 
       For Each kvp In subdic 
        dic(kvp.Key) = kvp.Value 
       Next 
      Case JTokenType.String 
       Try 
        dic(field.Name) = field.Value.ToObject(Of String) 
       Catch ex As Exception 
        dic(field.Name) = field.Value.ToObject(Of Object) 
       End Try 
      Case JTokenType.TimeSpan 
       dic(field.Name) = field.Value.ToObject(Of TimeSpan) 
      Case Else 
       dic(field.Name) = field.Value.ToString 
     End Select 
    Next 
    Return dic 
End Function 

Sie müssen dies beachten, wenn oben mit Code:

  1. Es verwendet Rekursion eine Mehrverzweigungsstruktur zu glätten.So

    { 
        "A":"aaaa", 
        "B":"bbbb", 
        "C":{ 
          "D":"dddd", 
          "E":"eeee", 
          "F":"ffff" 
         } 
        } 
    } 
    

    wird sich

    A |B |D |E |F 
    ----+----+----+----+---- 
    aaaa|bbbb|dddd|eeee|ffff 
    
  2. Die Art und Weise habe ich davon ausgegangen, es wird nicht Vervielfältigungen, wenn Abflachung; Sollte es diese geben, wird es nur die letzte erhalten. So

    { 
        "A":"aaaa", 
        "B":"bbbb", 
        "C":{ 
          "D":"d1d1", 
          "E":"e1e1", 
          "F":"f1f1" 
         }, 
        "G":{ 
          "D":"d2d2", 
          "E":"e2e2", 
          "F":"f2f2" 
         } 
        } 
    } 
    

    wird sich

    A |B |D |E |F 
    ----+----+----+----+---- 
    aaaa|bbbb|d2d2|e2e2|f2f2 
    

    das ist eine offensichtliche Mängel, Buggy-Verhalten, die eine anspruchsvollere Ansatz erfordert, dass ich für Sie hinterlassen auf meinem Grund auf neu zu bauen.

+0

Ich komme, wohin du gehst, und warst in der Lage, dies zu sehen. Nur um klar zu sein, verwendet dies nicht wirklich "JsonPath" richtig? –

+0

Es verwendet JsonPath einmal am Anfang. Ihr Beispiel ließ mich jedoch denken, dass einfache Iterationen es von dem Punkt her schaffen könnten, an dem wir eine Sammlung von datensatzähnlichen Objekten haben. Bitte beachten Sie jedoch die Einschränkungen meines Codes. Es gibt viel Raum für Verbesserungen. – VBobCat

+0

Richtig, aber es hat mich auf die richtige Spur gebracht. Die anschließende Deserialisierung zum Wörterbuch war der eigentliche Trick. Weil ich mich selbst hasse, spiele ich auch damit, auf diesen Servern zu posten, was zu dieser [Frage] führt (http://stackoverflow.com/questions/43097829/posting-an-array-parameter-using-resharp). wenn du einen Moment hast. –

1

Es ist möglich DataColumns zu einem DataTable auch hinzufügen, wenn es bereits DataRows enthält.

Ich mache JSON nicht viel, aber mein allgemeiner Ansatz mit dubiosen XML ist in einen Strom von Schlüssel-Wert-Paaren zu zerlegen, wobei der Schlüssel die XPATH- "Adresse" ist und der Wert der Inhalt des Knotens ist (ausgenommen Kind-Knoten), dann durchlaufen Sie den Stream, um die DataTable zu erstellen. Vielleicht kann hier ein ähnlicher Ansatz mit JSONPath gemacht werden.

+0

Können Sie ein Beispiel dafür aufstellen, auch wenn Sie XML verwenden? Ich mache mich jetzt mit JSONPath vertraut. Im Moment stoße ich auf ein Problem, das über den dritten Knoten hinausgeht (dh von oben, 'form12873'->' data'-> 'value' -> {Werte}), wenn die Werte selbst ein Array sind. –

+0

Ich fügte ein funktionierendes JSON-Beispiel am Ende ohne meine hinzugefügte Markierung hinzu, das sollte für das Testen verwendbar sein. Ich komme immer wieder dazu, Fehler nicht zu deserialisieren, egal ob ich mich mit dem Knoten als Jobject oder Jarray befasse. Wenn Sie ein Beispiel mit der obigen Zeichenfolge machen könnten, würde ich akzeptieren. –