2016-03-21 4 views
2

Ich erhalte ein unregelmäßiges JSON-Array aus der öffentlichen API des Census Bureau. Die Variablennamen sind alle im ersten Element, und ich habe Probleme, es zu deserialisieren.Deserialisieren von JSON-Arrays mit Variablennamen zuerst mit C# Json.NET

http://api.census.gov/data/2014/pep/agesex?get=AGE,POP,SEX&for=us:*&DATE=7

JSON gibt mir wie folgt aus:

[["AGE","POP","SEX","DATE","us"], 
["0","3948350","0","7","1"], 
["1","3962123","0","7","1"], 
["2","3957772","0","7","1"], 
["3","4005190","0","7","1"], 
["4","4003448","0","7","1"], 
["5","4004858","0","7","1"], 
["6","4134352","0","7","1"], 
["7","4154000","0","7","1"]] 

ich erfolgreich diese mit deserialisieren kann:

var test1 = JsonConvert.DeserializeObject<String[][]>(jsonStr); 

aber ich versuche es zu einer Klasse wie folgt deserialisieren :

public class TestClass 
{ 
    public string AGE { get; set; } 
    public string POP { get; set; } 
    public string SEX { get; set; } 
    public string DATE { get; set; } 
    public string us { get; set; } 
} 

Ich versuche, dies zu tun:

var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr); 

Aber ich bin die folgende Ausnahme erhalten:

Eine Ausnahme vom Typ 'Newtonsoft.Json.JsonSerializationException' aufgetreten in Newtonsoft.Json. dll wurde aber nicht im Benutzercode behandelt

Weitere Informationen: Listentyp TestClass kann nicht erstellt und ausgefüllt werden. Pfad ‚[0]‘, Zeile 1, Position 2.

+0

Ich bin ziemlich sicher, Sie Ich muss es als ein Array von Arrays deserialisieren und es dann in das gewünschte Format konvertieren. – AndyJ

+2

Hier ist ein Beispiel dafür: https://dotnetfiddle.net/Cr0aRL – AndyJ

+1

@AndyJ deine Geige sieht ganz nett aus. Möchten Sie es hier in eine Antwort umwandeln? – derpirscher

Antwort

6

Es gibt zwei Teile dazu.

Zuerst wird der JSON in Daten umgewandelt, die in C# verwendbar sind, und der zweite wandelt diese Daten in nette Objekte um.

Hier ist ein Arbeits dotNetFiddle.net Beispiel den folgenden Code: https://dotnetfiddle.net/Cr0aRL

Jede Zeile in Ihrem JSON aus einem Array von Strings gemacht wird. Das ist also ein Array aus einem Array von Strings. In C#, das als String [] [] geschrieben werden kann.

Also, um die JSON in nutzbare Daten mit JSON zu verwandeln.Net können Sie tun:

var json = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"],[\"0\",\"3948350\",\"0\",\"7\",\"1\"],[\"1\",\"3962123\",\"0\",\"7\",\"1\"],[\"2\",\"3957772\",\"0\",\"7\",\"1\"],[\"3\",\"4005190\",\"0\",\"7\",\"1\"],[\"4\",\"4003448\",\"0\",\"7\",\"1\"],[\"5\",\"4004858\",\"0\",\"7\",\"1\"],[\"6\",\"4134352\",\"0\",\"7\",\"1\"],[\"7\",\"4154000\",\"0\",\"7\",\"1\"]]"; 
    var rawData = JsonConvert.DeserializeObject<string[][]>(json); 

Als nächstes wird diese Daten in Objekte.

Die erste Zeile ist die Kopfzeile, die die Spaltennamen enthält, also wollen wir das holen und dann den Spaltenindex für jeden Spaltennamen herausfinden.

Jetzt haben wir die Indizes, wir müssen jede Zeile nehmen und in das entsprechende Objekt konvertieren. Ich habe LINQ dafür verwendet, da es sehr gut darin ist, die Datenverarbeitung klar darzustellen.

Endlich ein bisschen testen, um sicherzustellen, dass Sie die Daten haben, die Sie erwarten.

//Get the second data row as an example 
    var example = testData.Skip(1).First(); 

    //Output example POP to check value 
    Console.WriteLine(example.POP); 

Alles oben ist sehr manuell.

Sie müssen wissen, welche Header Sie erwarten, dann suchen Sie die Indizes manuell, dann ordnen Sie die Zeilen den Objekten manuell zu.

Es ist durchaus möglich für einen einfachen Anwendungsfall, dass das in Ordnung ist. In größeren und/oder komplexeren Systemen möchten/müssen Sie diese Schritte jedoch automatisieren.

Diese Schritte zu automatisieren ist möglich, aber es geht über den Rahmen dieser Antwort hinaus, da Ihre Herangehensweise von vielen verschiedenen Faktoren abhängen kann.

2

Sie haben die Verarbeitung auf eigener Faust zu tun, denn es gibt keine Möglichkeit, die json Deserializer wissen können, wie die Werte in betreffende Variablen zu setzen .

Wenn Sie wissen, wird dies genau diese Struktur sein, könnten Sie zum Beispiel fügen Sie einen entsprechenden Konstruktor

public TestClass(string[] values) { 
    AGE = values[0]; 
    ... 
} 

Ihrer Klasse. Dann serialisieren Sie Ihr Ergebnis in Array von Arrays von String und übergeben Sie dann die inneren Arrays an Ihren Konstruktor.

var t1 = JsonConvert.DeserializeObject<string[][]>(jsonStr); 
//skip the first entry, as this contains the headers 
var t2 = t1.Skip(1).Select(x=> new TestClass(x)); 

Wenn Ihre Struktur variiert, müssen Sie etwas komplexeren Mapping-Code schreiben.

+0

Danke. Ich hatte Angst davor. Ich hatte gehofft, dass es eine Deserializer-Einstellung gab, die mir nicht bewusst war. Ich denke, Ihre ist die einfachste Lösung und gibt mir ein paar Möglichkeiten zur Validierung. – MothraTL

+0

@JamesDev Ziemlich sicher, es tut https: // dotnetfiddle.net/VouNeM – derpirscher

1

Sie müssen einige benutzerdefinierte Zuordnung vornehmen, da Ihr Json keine Namenskonventionen hat, so dass Sie mit den Daten in Array- und Indexformaten arbeiten müssen. Das wird funktionieren:

var jsonStr = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"], [\"0\",\"3948350\",\"0\",\"7\",\"1\"], [\"1\",\"3962123\",\"0\",\"7\",\"1\"], [\"2\",\"3957772\",\"0\",\"7\",\"1\"], [\"3\",\"4005190\",\"0\",\"7\",\"1\"], [\"4\",\"4003448\",\"0\",\"7\",\"1\"], [\"5\",\"4004858\",\"0\",\"7\",\"1\"], [\"6\",\"4134352\",\"0\",\"7\",\"1\"], [\"7\",\"4154000\",\"0\",\"7\",\"1\"]]"; 
var test2 = JsonConvert.DeserializeObject<string[][]>(jsonStr); 
var test3 = test2.Select(x => new TestClass() 
{ 
    AGE = x[0].ToString(), 
    POP = x[1].ToString(), 
    SEX = x[2].ToString(), 
    DATE = x[3].ToString(), 
    us = x[4].ToString() 
}).ToList(); 
+1

Sie sollten das erste Element des Arrays überspringen, da es die Header mit Variablennamen enthält. – derpirscher

3

Sie könnten eine benutzerdefinierte JsonConverter erstellen, um diese Konvertierung während der Deserialisierung zu verarbeiten. Der Umwandlungscode unterscheidet sich hier nicht wirklich von anderen Antworten, außer dass er in eine separate Klasse gekapselt ist, damit Sie Ihren Hauptcode nicht mit den Umwandlungsdetails matschig machen. Aus der Sicht Ihres Hauptcodes funktioniert es "einfach".

Hier ist, wie die Konverter zu schreiben:

public class TestClassArrayConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(TestClass[])); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JArray table = JArray.Load(reader); 
     TestClass[] items = new TestClass[table.Count - 1]; 
     for (int i = 1; i < table.Count; i++) 
     { 
      JArray row = (JArray)table[i]; 
      items[i - 1] = new TestClass 
      { 
       AGE = (string)row[0], 
       POP = (string)row[1], 
       SEX = (string)row[2], 
       DATE = (string)row[3], 
       us = (string)row[4] 
      }; 
     } 
     return items; 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Und hier ist, wie Sie es verwenden würde:

var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr, new TestClassArrayConverter()); 

Fiddle: https://dotnetfiddle.net/68Q0KT

+0

Schön! Wenn Sie möchten, dass die JSON.Net-Serialisierung "nur funktioniert", dann ist dies eine großartige Option. – AndyJ

0
//test Case 

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Collections.Generic; 
namespace ApiController.Test 
{ 
    [TestClass] 
    public class DownloadIrregularJsonStringObjects 
    { 
     string ApiKey => "YourPersonalCensusKey"; 

     /// <summary> 
     /// You have to get your own ApiKey from the Census Website 
     /// </summary>  
     [TestMethod] 
     public void TestGetItem() 
     { 
     string url = $"http://api.census.gov/data/timeseries/healthins/sahie?get=NIC_PT,NAME,NUI_PT&for=county:*&in=state:*&time=2015&key={YourPersonalCensusKey}"; 
     string expected = "Autauga County, AL"; 
     IList<HealthData> actual = ApiController.DownloadIrregularJsonStringObjects.GetCensusHealthData(url); 
     Assert.AreEqual(actual[0].NAME, expected); 
    } 
} 
} 

///Actual Assembly 

using Newtonsoft.Json; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 

namespace ApiController 
{ 
    public class DownloadIrregularJsonStringObjects 
    { 
     public static IList<HealthData> GetCensusHealthData(string url) 
     { 
      var json = GetData(url); 
      var rawData = JsonConvert.DeserializeObject<string[][]>(json); 

      var headerRow = rawData.First(); 

      var nic_pt_Index = Array.IndexOf(headerRow, "NIC_PT"); 
      var name_Index = Array.IndexOf(headerRow, "NAME"); 
      var nui_pt_Index = Array.IndexOf(headerRow, "NUI_PT"); 

      IList<HealthData> retVal = new List<HealthData>(); 

      foreach (var r in rawData.Skip(1)) 
      { 
       HealthData dataRow = new HealthData(); 
       dataRow.NIC_PT = r[nic_pt_Index]; 
       dataRow.NAME = r[name_Index]; 
       dataRow.NUI_PT = r[nui_pt_Index]; 
       retVal.Add(dataRow);     
      } 
      return retVal; 
     } 

    private static string GetData(string url) 
    { 
     using (var w = new WebClient()) 
     { 
      var jsonData = string.Empty; 
      jsonData = w.DownloadString(url); 

      return jsonData; 
     } 
    } 
} 
public class HealthData 
{ 
    public string NIC_PT { get; set; } 
    public string NAME { get; set; } 
    public string NUI_PT { get; set; }  

} 
} 
+0

bitte erklären Sie die Antwort ein wenig – didiz

Verwandte Themen