2017-03-19 1 views
2

OK, ich werde versuchen, kurz und spezifisch zu sein. Ich bin einen JSON-String aus einer öffentlichen API erhalten, die wie folgt aussieht: (kurze Version davon, dauerte nur 2 Proben)Deserialize JSON String zu C# Klassen

[{ 
    "$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities", 
    "id": "bakerloo", 
    "name": "Bakerloo", 
    "modeName": "tube", 
    "disruptions": [], 
    "created": "2017-03-16T15:56:01.01Z", 
    "modified": "2017-03-16T15:56:01.01Z", 
    "lineStatuses": [ 
     { 
     "$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities", 
     "id": 0, 
     "statusSeverity": 10, 
     "statusSeverityDescription": "Good Service", 
     "created": "0001-01-01T00:00:00", 
     "validityPeriods": [] 
     } 
    ], 
    "routeSections": [], 
    "serviceTypes": [ 
     { 
     "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities", 
     "name": "Regular", 
     "uri": "/Line/Route?ids=Bakerloo&serviceTypes=Regular" 
     } 
    ], 
    "crowding": { 
     "$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities" 
    } 
    }, 
    { 
    "$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities", 
    "id": "central", 
    "name": "Central", 
    "modeName": "tube", 
    "disruptions": [], 
    "created": "2017-03-16T15:56:01.01Z", 
    "modified": "2017-03-16T15:56:01.01Z", 
    "lineStatuses": [ 
     { 
     "$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities", 
     "id": 0, 
     "statusSeverity": 10, 
     "statusSeverityDescription": "Good Service", 
     "created": "0001-01-01T00:00:00", 
     "validityPeriods": [] 
     } 
    ], 
    "routeSections": [], 
    "serviceTypes": [ 
     { 
     "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities", 
     "name": "Regular", 
     "uri": "/Line/Route?ids=Central&serviceTypes=Regular" 
     }, 
     { 
     "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities", 
     "name": "Night", 
     "uri": "/Line/Route?ids=Central&serviceTypes=Night" 
     } 
    ], 
    "crowding": { 
     "$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities" 
    } 
    }] 

so weit so gut, unten Ich werde den entsprechenden Code einfügen, die ich bin Verwenden und versuchen, diesen JSON-String in C# -Klassen zu deserialisieren, die ich von json2csharp kostenlosen Online-Service erhalten habe. Der entsprechende Code, wo ich versuche, dies zu erreichen ist:

public async static Task<tubeStatusRootObject> GetTubeStatus(string url) 
{ 
    var http = new HttpClient(); 
    var response = await http.GetAsync(url); 
    var result = await response.Content.ReadAsStringAsync(); //This is working 
    var deserializer = new DataContractJsonSerializer(typeof(tubeStatusRootObject)); 
    var ms = new MemoryStream(Encoding.UTF8.GetBytes(result)); 
    var data = (tubeStatusRootObject)deserializer.ReadObject(ms); 
    return data; //all "data" properties are null 
} 

, wie Sie die Kommentare oben lesen können, die alle in Daten immer ich vor der Rückkehr ist null.

Die Klassen, die von json2csharp generiert wurden, sieht wie folgt aus:

[DataContract] 
public class Disruption 
{ 
    [DataMember] 
    public string type { get; set; } 

    [DataMember] 
    public string category { get; set; } 

    [DataMember] 
    public string categoryDescription { get; set; } 

    [DataMember] 
    public string description { get; set; } 

    [DataMember] 
    public string additionalInfo { get; set; } 

    [DataMember] 
    public string created { get; set; } 

    [DataMember] 
    public List<object> affectedRoutes { get; set; } 

    [DataMember] 
    public List<object> affectedStops { get; set; } 

    [DataMember] 
    public string closureText { get; set; } 

    [DataMember] 
    public bool? isWholeLine { get; set; } 
} 

[DataContract] 
public class LineStatus 
{ 
    [DataMember] 
    public string type { get; set; } 

    [DataMember] 
    public int id { get; set; } 

    [DataMember] 
    public int statusSeverity { get; set; } 

    [DataMember] 
    public string statusSeverityDescription { get; set; } 

    [DataMember] 
    public string created { get; set; } 

    [DataMember] 
    public List<object> validityPeriods { get; set; } 

    [DataMember] 
    public string lineId { get; set; } 

    [DataMember] 
    public string reason { get; set; } 

    [DataMember] 
    public Disruption disruption { get; set; } 
} 

[DataContract] 
public class ServiceType 
{ 
    [DataMember] 
    public string type { get; set; } 

    [DataMember] 
    public string name { get; set; } 

    [DataMember] 
    public string uri { get; set; } 
} 

[DataContract] 
public class Crowding 
{ 
    [DataMember] 
    public string type { get; set; } 
} 

[DataContract] 
public class tubeStatusRootObject 
{ 
    [DataMember] 
    public string type { get; set; } 

    [DataMember] 
    public string id { get; set; } 

    [DataMember] 
    public string name { get; set; } 

    [DataMember] 
    public string modeName { get; set; } 

    [DataMember] 
    public List<object> disruptions { get; set; } 

    [DataMember] 
    public string created { get; set; } 

    [DataMember] 
    public string modified { get; set; } 

    [DataMember] 
    public List<LineStatus> lineStatuses { get; set; } 

    [DataMember] 
    public List<object> routeSections { get; set; } 

    [DataMember] 
    public List<ServiceType> serviceTypes { get; set; } 

    [DataMember] 
    public Crowding crowding { get; set; } 
} 

offensichtlich habe ich nur noch die [DataContract] 's und [DataMember]' dort s. Jeder, der Idee von dem, was ich falsch mache und mir helfen könnte

Ich habe das Beispiel gefolgt von Channel9

Bitte Duplikat nicht aus, als ich viele ähnliche Fragen gefunden haben, einige mit newtonsoft json aber ich konnte die Lösungen von dort nicht in mein Beispiel implementieren

+0

Warum können Sie nicht newtonsoft verwenden? – fredrik

+0

Es ist nicht, dass ich nicht kann, ich folgte nur dem verknüpften Beispiel und ich möchte verstehen, wie es funktioniert, oder in meinem Fall, warum es nicht funktioniert :) – al1en

+0

Sie sagten, dass Sie nicht mit newtonsoft arbeiten können, nicht nicht . Riesige Diff ... – fredrik

Antwort

1

Es ist ein bisschen kompliziertes Objekt, das Sie bekommen.

Wenn es deserialisiert ist (wie Sie es jetzt tun), sucht es nach übereinstimmenden Namen von Objekten mit demselben Datentyp wie erwartet. Wenn es nicht gefunden wird, schlägt die Deserialisierung fehl und gibt null zurück. Und es ist ziemlich sicher.

Wenn nicht newtonsoft, können Sie den Datentyp jedes verschachtelten Objekts mit einem generischen Objekt vergleichen. Oder müssen Sie einige Zeichenfolgenoperationen ausführen, um es selbst zu deserialisieren (ziemlich kompliziert).

würde ich für solche Objekte mit Newtonsoft.json bevorzugen

+0

das ganze Objekt, das ich bekomme, können Sie es hier sehen https://api.tfl.gov.uk/Line/Mode/tube/Status - aber das Objekt selbst wird jedes Mal anders sein, hängt von einigen Störungen und ähnlichen Änderungen ab die Zeilen – al1en

2

Die Probe zur Verfügung gestellten Daten ein Array beschreibt (tubeStatusRootObject[]) aber wenn Sie versuchen Sie es, die auf eine einzelne Instanz zu gieße deserialisieren ein ungültiger gegossen wird. deshalb ist datanull.

Auch keine Notwendigkeit, das Rad neu zu erfinden, wenn es Werkzeuge gibt, um das Problem einfach zu lösen.

static http = new HttpClient(); //reuse httpclient instead of creating a new one each time. 
public async static Task<tubeStatusRootObject[]> GetTubeStatus(string url) { 
    var response = await http.GetAsync(url); 
    var json = await response.Content.ReadAsStringAsync(); //This is working 
    var data = Newtonsoft.Json.JsonConvert.DeserializeObject<tubeStatusRootObject[]>(json); 
    return data; 
} 
+0

Ich habe meinen Code geändert, um Newtonsoft.Json zu implementieren, und ich habe das Ergebnis zurückbekommen. Es gibt also keine einfache Möglichkeit, dasselbe Ergebnis zu erzielen, ohne Newtonsoft zu implementieren. – al1en

+2

@ al1en, Sicher andere Möglichkeiten existieren, aber es selbst wird viel komplizierter. – Nkosi

+0

Es soll das deserialisierte Objekt geben. und da ist es !! : D Ziemlich einfach mit Newtonsoft! –

1

Wie andere gesagt haben, müssen Sie in ein Array deserialisieren, nicht nur eine einzelne Instanz des Typs Sie als Antwort definiert haben, ist ein Array.

Wenn Sie die Antwort in einen String lesen dann Json.Net bedeutet, dass Sie nur eine einzige Zeile Antwort benötigen

var data= Newtonsoft.Json.JsonConvert.DeserializeObject<tubeStatusRootObject[]>(result); 

dies mit 3 + Linien verglichen wird, wenn DataContractJsonSerializer mit

var deserializer = new DataContractJsonSerializer(typeof(tubeStatusRootObject[])); 
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(result))) 
{ 
    var data = (tubeStatusRootObject[])deserializer.ReadObject(ms); 
} 

(Hinweis die Verwendung des Wrappers using, um sicherzustellen, dass der MemoryStream entsorgt wird.)

Wenn Sie den HTTP-Antwortstream direkt in den Deserializer lesen, erhalten Sie den LoC-Speicher jedoch nicht.

using (var s = http.GetStreamAsync(url).Result) 
using (var sr = new StreamReader(s)) 
using (var reader = new Newtonsoft.Json.JsonTextReader(sr)) 
{ 
    var serializer = new Newtonsoft.Json.JsonSerializer(); 
    var data = serializer.Deserialize<tubeStatusRootObject[]>(reader); 
} 

Versus

using (var stream = await response.Content.ReadAsStreamAsync()) 
{ 
    var dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(tubeStatusRootObject[])); 

    var data= (tubeStatusRootObject[])dcjs.ReadObject(stream); 
} 

Die andere Sache, die Sie wünschen können ist die Leistung zu berücksichtigen. Json.Net claim the following

Json Serializers performance comparison graph