Désérialisation de JSON en objet .NET à l'aide de Newtonsoft (ou LINQ to JSON peut-être?)

318

Je sais qu'il y a quelques articles sur Newtonsoft, donc j'espère que ce n'est pas exactement une répétition ... J'essaie de convertir les données JSON retournées par l'API de Kazaa en un bel objet en quelque sorte

WebClient client = new WebClient();
Stream stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album");
StreamReader reader = new StreamReader(stream);

List<string> list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(reader.Read().ToString());

foreach (string item in list)
{
    Console.WriteLine(item);
}

//Console.WriteLine(reader.ReadLine());
stream.Close();

Cette ligne JsonConvert n'est que la plus récente que j'essayais ... Je ne comprends pas tout à fait et j'espérais éliminer un peu de jeu de jambes en vous demandant les gars. J'essayais à l'origine de le convertir en un dictionnaire ou quelque chose ... et en fait, j'ai juste besoin d'y accrocher quelques valeurs, à en juger par la documentation, peut-être que LINQ to JSON de Newtonsoft pourrait être un meilleur choix? Pensées / Liens?

Voici un exemple des données de retour JSON:

{
  "page": 1,
  "total_pages": 8,
  "total_entries": 74,
  "q": "muse",
  "albums": [
    {
      "name": "Muse",
      "permalink": "Muse",
      "cover_image_url": "http://image.kazaa.com/images/69/01672812 1569/Yaron_Herman_Trio/Muse/Yaron_Herman_Trio-Muse_1.jpg",
      "id": 93098,
      "artist_name": "Yaron Herman Trio"
    },
    {
      "name": "Muse",
      "permalink": "Muse",
      "cover_image_url": "htt p://image.kazaa.com/images/54/888880301154/Candy_Lo/Muse/Candy_Lo-Muse_1.jpg",
      "i d": 102702,
      "artist_name": "\u76e7\u5de7\u97f3"
    },
    {
      "name": "Absolution",
      "permalink": " Absolution",
      "cover_image_url": "http://image.kazaa.com/images/65/093624873365/Mus e/Absolution/Muse-Absolution_1.jpg",
      "id": 48896,
      "artist_name": "Muse"
    },
    {
      "name": "Ab solution",
      "permalink": "Absolution-2",
      "cover_image_url": "http://image.kazaa.com/i mages/20/825646911820/Muse/Absolution/Muse-Absolution_1.jpg",
      "id": 118573,
      "artist _name": "Muse"
    },
    {
      "name": "Black Holes And Revelations",
      "permalink": "Black-Holes-An d-Revelations",
      "cover_image_url": "http://image.kazaa.com/images/66/093624428466/ Muse/Black_Holes_And_Revelations/Muse-Black_Holes_And_Revelations_1.jpg",
      "id": 48813,
      "artist_name": "Muse"
    },
    {
      "name": "Black Holes And Revelations",
      "permalink": "Bla ck-Holes-And-Revelations-2",
      "cover_image_url": "http://image.kazaa.com/images/86/ 825646911486/Muse/Black_Holes_And_Revelations/Muse-Black_Holes_And_Revelations_1 .jpg",
      "id": 118543,
      "artist_name": "Muse"
    },
    {
      "name": "Origin Of Symmetry",
      "permalink": "Origin-Of-Symmetry",
      "cover_image_url": "http://image.kazaa.com/images/29/825646 912629/Muse/Origin_Of_Symmetry/Muse-Origin_Of_Symmetry_1.jpg",
      "id": 120491,
      "artis t_name": "Muse"
    },
    {
      "name": "Showbiz",
      "permalink": "Showbiz",
      "cover_image_url": "http: //image.kazaa.com/images/68/825646182268/Muse/Showbiz/Muse-Showbiz_1.jpg",
      "id": 60444,
      "artist_name": "Muse"
    },
    {
      "name": "Showbiz",
      "permalink": "Showbiz-2",
      "cover_imag e_url": "http://image.kazaa.com/images/50/825646912650/Muse/Showbiz/Muse-Showbiz_ 1.jpg",
      "id": 118545,
      "artist_name": "Muse"
    },
    {
      "name": "The Resistance",
      "permalink": "T he-Resistance",
      "cover_image_url": "http://image.kazaa.com/images/36/825646864836/ Muse/The_Resistance/Muse-The_Resistance_1.jpg",
      "id": 121171,
      "artist_name": "Muse"
    }
  ],
  "per_page": 10
}

J'ai fait un peu plus de lecture et j'ai trouvé que LINQ to JSON de Newtonsoft était exactement ce que je voulais ... en utilisant WebClient, Stream, StreamReader et Newtonsoft ... Je peux frapper Kazaa pour les données JSON, extraire une URL, télécharger le fichier et le faire le tout comme sept lignes de code! J'aime cela.

WebClient client = new WebClient();
Stream stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album");
StreamReader reader = new StreamReader(stream);

Newtonsoft.Json.Linq.JObject jObject = Newtonsoft.Json.Linq.JObject.Parse(reader.ReadLine());

// Instead of WriteLine, 2 or 3 lines of code here using WebClient to download the file
Console.WriteLine((string)jObject["albums"][0]["cover_image_url"]);
stream.Close();

Ce message reçoit tellement de hits que j'ai pensé qu'il pourrait être utile d'inclure les bits "d'utilisation" qui sont discutés dans les commentaires.

using(var client = new WebClient())
using(var stream = client.OpenRead("http://api.kazaa.com/api/v1/search.json?q=muse&type=Album"))
using (var reader = new StreamReader(stream))
{
    var jObject = Newtonsoft.Json.Linq.JObject.Parse(reader.ReadLine());
    Console.WriteLine((string) jObject["albums"][0]["cover_image_url"]);
}
J Benjamin
la source
6
Exemple lisse, merci. Juste une suggestion: vous avez peut-être laissé cela pour plus de brièveté, mais depuis WebClient, Streamet StreamReadertous les implémentations IDisposable, vous voudrez peut-être ajouter quelques usingblocs à votre code.
arcain
ah oui, bon appel ... (ouais c'était en fait juste une application console que j'exécutais très vite pour rechercher les tâches que je venais de faire) ... lol
J Benjamin
1
+1 Merci d'avoir publié l'exemple Linq. Exactement ce dont j'avais besoin.
Mark Wilkins
La solution newtonsoft ne désérialise-t-elle pas entièrement le JSON également? Tout comme la solution de @ arcain.
AXMIM
Notez le lien ici: LINQ to JSON
yu yang Jian

Réponses:

259

Si vous avez juste besoin d'obtenir quelques éléments de l'objet JSON, j'utiliserais la JObjectclasse LINQ to JSON de Json.NET . Par exemple:

JToken token = JObject.Parse(stringFullOfJson);

int page = (int)token.SelectToken("page");
int totalPages = (int)token.SelectToken("total_pages");

J'aime cette approche car vous n'avez pas besoin de désérialiser complètement l'objet JSON. Cela est pratique avec les API qui peuvent parfois vous surprendre avec des propriétés d'objet manquantes, comme Twitter.

Documentation: Sérialisation et désérialisation de JSON avec Json.NET et LINQ to JSON avec Json.NET

arcain
la source
1
ya, j'ai en fait fait un peu plus de lecture et de tests ... j'ai trouvé que c'était aussi une bonne façon de le faire ... Newtonsoft, une jolie bibliothèque, je vais poster mon exemple pour les autres
J Benjamin
1
posté un exemple approximatif de la façon dont je le faisais ... pas tout à fait la même chose, je vois que vous avez suggéré JToken.Parse ... vous n'êtes pas encore sûr des différences entre les deux mais oui, bonnes choses!
J Benjamin
1
@Jbenjamin Merci! C'était une faute de frappe. JToken est la classe de base de JObject, et c'est juste ma préférence personnelle de travailler avec le type plus abstrait. Merci d'avoir appelé cela à mon attention.
arcain
Désolé, mais devrait-il s'agir de JToken ou de JObject? Le code ci-dessus renvoie toujours l'erreur "Erreur de lecture de JObject depuis JsonReader" de temps en temps.
TYRONEMICHAEL
1
@Tyrone Bien sûr, pas de problème. En fait, j'utilise également ce code pour l'analyse de l'état de Twitter, et j'ai dû écrire un peu de gestion des erreurs autour des appels à Twitter, car ils peuvent parfois être irréguliers. Si vous ne le faites pas déjà, je vous recommande de vider la réponse JSON brute de Twitter dans un journal avant d'essayer de l'analyser. Ensuite, si cela échoue, vous pouvez au moins voir si vous avez reçu quelque chose de génial sur le fil.
arcain
272

Vous pouvez utiliser le dynamictype C # pour faciliter les choses. Cette technique simplifie également la refacturation car elle ne repose pas sur des chaînes magiques.

JSON

La chaîne JSON ci-dessous est une réponse simple d'un appel d'API HTTP et définit deux propriétés: Idet Name.

{"Id": 1, "Name": "biofractal"}

C #

Utilisez JsonConvert.DeserializeObject<dynamic>()pour désérialiser cette chaîne en un type dynamique, puis accédez simplement à ses propriétés de la manière habituelle.

dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

Si vous spécifiez le type de la resultsvariable car dynamic, au lieu d'utiliser le varmot - clé, les valeurs de propriété se désérialiseront correctement, par exemple Iden un intet non en un JValue(merci à GFoley83 pour le commentaire ci-dessous).

Remarque : Le lien NuGet pour l'assembly Newtonsoft est http://nuget.org/packages/newtonsoft.json .

Package : Vous pouvez également ajouter le package avec nuget live installer, avec votre projet ouvert, parcourez simplement le package , puis installez-le simplement install, unistall, update , il sera simplement ajouté à votre projet sous Dependencies / NuGet

biofractal
la source
J'utilisais le même morceau de code que ci-dessus pour désérialiser la réponse Twitter avec newtonsoft.dll version 4.5.6 et cela fonctionnait bien ..mais après la mise à jour vers la version 5.0.6 .. il a commencé à lancer une erreur ... une idée Pourquoi ??
Pranav
1
Bon pour un objet dynamique, lorsque nous savons ou que nous avons une classe c # afin que nous puissions consommer en tant que classe C # lors du remplacement de dynamique, par exemple <Myclass>.
MSTdev
2
Utilisez dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json);ici FTW. Il se désérialise correctement Iden un int et non en un JValue. Voir ici: dotnetfiddle.net/b0WxGJ
GFoley83
@biofractal Comment pourrais-je faire cela dynamic results = JsonConvert.DeserializeObject<dynamic>(json); dans VB.NET? Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json)ne marche pas.
Flo
41

Avec le dynamicmot - clé, il devient vraiment facile d'analyser n'importe quel objet de ce type:

dynamic x = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString);
var page = x.page;
var total_pages = x.total_pages
var albums = x.albums;
foreach(var album in albums)
{
    var albumName = album.name;

    // Access album data;
}
Sushant Srivastava
la source
Je voulais savoir comment parcourir les résultats et cela a pris trop de temps à trouver ... merci !!
batoutofhell
22

Corrigez-moi si je me trompe, mais l'exemple précédent, je pense, est légèrement désynchronisé avec la dernière version de la bibliothèque Json.NET de James Newton.

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];
Rick Leitch
la source
1
merci pour votre réponse, Rick, qui ressemble aussi aux exemples que j'ai trouvés dans la documentation la plus récente.
J Benjamin
1
Ouais, depuis qu'arcain a corrigé la faute de frappe, mon commentaire semble maintenant juste: «(. J'ai initialement posté parce que je n'ai pas reconnu JToken.Parse.
Rick Leitch
1
Pas du tout compliqué - il y a certainement eu une erreur, et il y a toujours plus d'une façon de le faire. Soit dit en passant, ma version de Json.NET prend en charge la syntaxe à l'aide de l'indexeur JObject, mais le code que j'ai modifié pour ma réponse a été extrait du code utilisant une surcharge de la SelectTokenméthode afin que je puisse supprimer les exceptions si le jeton n'était pas trouvé JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch):, c'est de là que vient la verbosité.
arcain
18

Si, comme moi, vous préférez traiter des objets fortement typés ** allez avec:

MyObj obj =  JsonConvert.DeserializeObject<MyObj>(jsonString);

De cette façon, vous pouvez utiliser intellisense et compiler la vérification des erreurs de type de temps.

Vous pouvez facilement créer les objets requis en copiant votre JSON en mémoire et en le collant en tant qu'objets JSON (Visual Studio -> Édition -> Collage spécial -> Coller JSON en tant que classes).

Voir ici si vous n'avez pas cette option dans Visual Studio.

Vous devrez également vous assurer que votre JSON est valide. Ajoutez votre propre objet au début s'il ne s'agit que d'un tableau d'objets. c'est-à-dire { "obj": [{}, {}, {}]}

** Je sais que la dynamique facilite parfois les choses, mais je suis un peu désolé avec ça.

Guy Lowe
la source
1
Très bien mon approche préférée de la programmation. J'aime les objets typés forts. Merci, car j'ai utilisé et modifié ce code.
j.hull
11

Liste dynamique typée de manière lâche - Désérialiser et lire les valeurs

// First serializing
dynamic collection = new { stud = stud_datatable }; // The stud_datable is the list or data table
string jsonString = JsonConvert.SerializeObject(collection);


// Second Deserializing
dynamic StudList = JsonConvert.DeserializeObject(jsonString);

var stud = StudList.stud;
foreach (var detail in stud)
{
    var Address = detail["stud_address"]; // Access Address data;
}
Arun Prasad ES
la source
8

J'aime cette méthode:

using Newtonsoft.Json.Linq;
// jsonString is your JSON-formatted string
JObject jsonObj = JObject.Parse(jsonString);
Dictionary<string, object> dictObj = jsonObj.ToObject<Dictionary<string, object>>();

Vous pouvez désormais accéder à tout ce que vous voulez en utilisant le dictObjcomme dictionnaire. Vous pouvez aussi utiliserDictionary<string, string> si vous préférez obtenir les valeurs sous forme de chaînes.

Vous pouvez utiliser cette même méthode pour caster comme n'importe quel type d'objet .NET.

Blairg23
la source
2
Je trouve cette méthode très agréable pour deux raisons: 1) lorsque vous ne vous souciez pas du type de données (tout est une chaîne), et 2) il est pratique de travailler avec un dictionnaire des valeurs
netfed
7

De plus, si vous recherchez simplement une valeur spécifique imbriquée dans le contenu JSON, vous pouvez faire quelque chose comme ceci:

yourJObject.GetValue("jsonObjectName").Value<string>("jsonPropertyName");

Et ainsi de suite.

Cela pourrait aider si vous ne voulez pas supporter le coût de conversion de l'intégralité du JSON en un objet C #.

Tony
la source
2

j'ai recherché une Extionclass pour json:

 public static class JsonExtentions
    {
        public static string SerializeToJson(this object SourceObject) { return Newtonsoft.Json.JsonConvert.SerializeObject(SourceObject); }


        public static T JsonToObject<T>(this string JsonString) { return (T)Newtonsoft.Json.JsonConvert.DeserializeObject<T>(JsonString); }
}

Design pattern:

 public class Myobject
    {
        public Myobject(){}
        public string prop1 { get; set; }

        public static Myobject  GetObject(string JsonString){return  JsonExtentions.JsonToObject<Myobject>(JsonString);}
        public  string ToJson(string JsonString){return JsonExtentions.SerializeToJson(this);}
    }

Usage:

   Myobject dd= Myobject.GetObject(jsonstring);

                 Console.WriteLine(dd.prop1);
Sloomy
la source
1

Assez tard pour cette fête, mais je suis tombé sur ce problème moi-même aujourd'hui au travail. Voici comment j'ai résolu le problème.

J'accédais à une API tierce pour récupérer une liste de livres. L'objet a renvoyé un objet JSON massif contenant environ 20+ champs, dont j'avais seulement besoin de l'ID en tant qu'objet chaîne de liste. J'ai utilisé linq sur l'objet dynamique pour récupérer le champ spécifique dont j'avais besoin, puis je l'ai inséré dans mon objet chaîne List.

dynamic content = JsonConvert.DeserializeObject(requestContent);
var contentCodes = ((IEnumerable<dynamic>)content).Where(p => p._id != null).Select(p=>p._id).ToList();

List<string> codes = new List<string>();

foreach (var code in contentCodes)
{
    codes.Add(code?.ToString());
}
todd.pund
la source
0

Obtenez enfin le nom de l'état à partir de JSON

Je vous remercie!

Imports System
Imports System.Text
Imports System.IO
Imports System.Net
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.collections.generic

Public Module Module1
    Public Sub Main()

         Dim url As String = "http://maps.google.com/maps/api/geocode/json&address=attur+salem&sensor=false"
            Dim request As WebRequest = WebRequest.Create(url)
        dim response As WebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
        dim reader As New StreamReader(response.GetResponseStream(), Encoding.UTF8)
          Dim dataString As String = reader.ReadToEnd()

        Dim getResponse As JObject = JObject.Parse(dataString)

        Dim dictObj As Dictionary(Of String, Object) = getResponse.ToObject(Of Dictionary(Of String, Object))()
        'Get State Name
        Console.WriteLine(CStr(dictObj("results")(0)("address_components")(2)("long_name")))
    End Sub
End Module
iApps Creator India
la source