2017-03-19 1 views
2

OK, je vais essayer d'être court et précis. Je reçois une chaîne JSON d'une API publique qui ressemble à ceci: (version courte de celui-ci, seulement 2 échantillons pris)Désérialise la chaîne JSON en classes C#

[{ 
    "$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" 
    } 
    }] 

jusqu'ici ça va, je vais coller ci-dessous le code approprié que je suis en utilisant et en essayant de désérialiser cette chaîne JSON dans les classes C# que j'ai reçu du service en ligne gratuit json2csharp. Le code correspondant où je suis en train d'y parvenir est:

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 
} 

que vous pouvez lire les commentaires ci-dessus, tout ce que je reçois à l'intérieur des données avant le retour est nulle.

Les classes qui ont été générés par json2csharp ressemble à ceci:

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

évidemment Je viens d'ajouter les [DataContract] 's et [DataMember] est là. Toute personne avec l'idée de ce que je fais de mal et pourrait me aider

J'ai suivi l'exemple de Channel9

S'il vous plaît ne pas marquer en double, comme je l'ai trouvé beaucoup de questions similaires, certains utilisant newtonsoft JSON mais je ne pouvais pas implémenter les solutions à partir de là dans mon exemple

+0

Pourquoi ne pas utiliser newtonsoft? – fredrik

+0

Ce n'est pas que je ne peux pas, je viens de suivre l'exemple lié et je veux comprendre comment cela fonctionne, ou dans mon cas pourquoi ça ne fonctionne pas :) – al1en

+0

Vous avez dit que vous ne pouvez pas le faire fonctionner avec newtonsoft, pas ne sera pas . Énorme diff ... – fredrik

Répondre

1

C'est un objet un peu compliqué que vous obtenez.

Lorsqu'il est désérialisé (comme vous le faites actuellement), il recherche des noms d'objets correspondants avec le même type de données que prévu. Si ce n'est pas le cas, la désérialisation échoue et renvoie null. Et c'est assez sûr.

Si ce n'est pas newtonsoft, vous pouvez soit faire correspondre le type de données de chaque objet imbriqué à un générique. Ou besoin d'effectuer des opérations de cordes pour le désérialiser vous-même (assez compliqué).

je préférerais l'utilisation Newtonsoft.json pour de tels objets

+0

l'objet entier que je reçois vous pouvez le voir ici https://api.tfl.gov.uk/Line/Mode/tube/Status - mais l'objet lui-même sera différent à chaque fois, dépend de certaines perturbations et des changements similaires à les lignes – al1en

2

Les données fournies échantillons décrit un tableau (tubeStatusRootObject[]) mais quand vous essayez de vous désérialiser le jeter à une seule instance qui est une distribution non valide. c'est pourquoi data est null.

De même, il n'est pas nécessaire de réinventer la roue s'il existe des outils pour résoudre le problème.

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

J'ai changé mon code pour implémenter Newtonsoft.Json et j'ai récupéré le résultat. Il n'y a donc pas de moyen facile d'atteindre le même résultat sans implémenter Newtonsoft ??? – al1en

+2

@ al1en, Bien sûr, d'autres moyens existent mais le faire soi-même va être beaucoup plus compliqué. – Nkosi

+0

Soit l'objet désérialisé. et voilà! : D Assez facile avec Newtonsoft! –

1

Comme d'autres l'ont dit, vous devez désérialiser dans un tableau, et pas seulement une seule instance du type que vous avez défini comme la réponse est un tableau.

Si vous lisez la réponse dans une chaîne alors Json.Net signifie que vous avez seulement besoin d'une réponse unique ligne

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

c'est comparé à 3 + lignes lors de l'utilisation DataContractJsonSerializer

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

(note l'utilisation du wrapper using pour vous assurer que le MemoryStream est supprimé.

Si vous lisez le flux de réponse HTTP directement dans le désérialiseur, vous n'obtiendrez pas la sauvegarde de la LoC.

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

L'autre chose que vous pouvez envisager est la performance. Json.Net claim the following

Json Serializers performance comparison graph