Deserialización de objetos JSON a .NET usando Newtonsoft (¿o LINQ to JSON quizás?)

318

Sé que hay algunas publicaciones sobre Newtonsoft, así que espero que esto no sea exactamente una repetición ... Estoy tratando de convertir los datos JSON devueltos por la API de Kazaa en un buen objeto de algún tipo

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

Esa línea de JsonConvert es la más reciente que estaba intentando ... No lo estoy entendiendo y esperaba eliminar algo de juego de pies preguntándoles. Originalmente estaba tratando de convertirlo en un diccionario o algo así ... y en realidad, solo necesito enganchar un par de valores allí, a juzgar por la documentación, ¿tal vez el LINQ to JSON de Newtonsoft podría ser una mejor opción? Pensamientos / Enlaces?

Aquí hay un ejemplo de los datos de retorno 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
}

Leí un poco más y descubrí que LINQ to JSON de Newtonsoft es exactamente lo que quería ... usando WebClient, Stream, StreamReader y Newtonsoft ... Puedo acceder a Kazaa para obtener datos JSON, extraer una URL, descargar el archivo y hacerlo ¡todo en siete líneas de código! Me encanta.

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

Esta publicación recibe tantos éxitos que pensé que podría ser útil incluir los bits de "uso" que se discuten en los comentarios.

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
fuente
66
Buen ejemplo, gracias. Sólo una sugerencia: es posible que haya dejado este fuera por razones de brevedad, pero ya WebClient, Streamy StreamReadertodo poner en práctica IDisposable, es posible que desee agregar algunos usingbloques a su código.
arcain
ah sí, buena llamada ... (sí, esto en realidad era solo una aplicación de consola que estaba ejecutando muy rápido para investigar las tareas que tengo por delante) Ahora a investigar la última pieza del rompecabezas, cifrado HLS + AES :) ugh ... jajaja
J Benjamin
1
+1 Gracias por publicar el ejemplo de Linq. Exactamente lo que necesitaba.
Mark Wilkins
¿La solución newtonsoft no deserializa completamente el JSON también? Al igual que la solución de @ arcain.
AXMIM
Tenga en cuenta el enlace aquí: LINQ to JSON
yu yang Jian

Respuestas:

259

Si solo necesita obtener algunos elementos del objeto JSON, usaría la JObjectclase LINQ to JSON de Json.NET . Por ejemplo:

JToken token = JObject.Parse(stringFullOfJson);

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

Me gusta este enfoque porque no necesita deserializar completamente el objeto JSON. Esto es útil con API que a veces pueden sorprenderlo con las propiedades de objetos faltantes, como Twitter.

Documentación: serialización y deserialización de JSON con Json.NET y LINQ to JSON con Json.NET

arcain
fuente
1
sí, he leído un poco más y he probado ... descubrí que esta también es una buena manera de hacerlo ... Newtonsoft, una biblioteca bastante agradable, publicaré mi ejemplo para otros
J Benjamin
1
publiqué un ejemplo aproximado de cómo lo estaba haciendo ... no es lo mismo, veo que sugirió JToken.Parse ... aún no estoy seguro de las diferencias entre los dos, pero sí, ¡cosas buenas!
J Benjamin
1
@Jbenjamin Gracias! Eso fue un error tipográfico. JToken es la clase base para JObject, y es solo mi preferencia personal trabajar con el tipo más abstracto. Gracias por llamarme la atención.
arcain
Lo siento, pero ¿debería ser JToken o JObject? El código anterior sigue arrojando el error "Error al leer JObject de JsonReader" de vez en cuando.
TYRONEMICHAEL
1
@Tyrone Claro, no hay problema. De hecho, también uso este código para analizar el estado de Twitter, y he tenido que escribir un poco de manejo de errores en las llamadas a Twitter, ya que a veces pueden ser irregulares. Si aún no lo está haciendo, le recomendaría que descargue la respuesta JSON sin procesar de Twitter en un registro antes de intentar analizarla. Luego, si falla, al menos puede ver si recibió algo funky por cable.
arcain
272

Puede usar el dynamictipo C # para facilitar las cosas. Esta técnica también simplifica la refactorización, ya que no se basa en cadenas mágicas.

JSON

La siguiente cadena JSON es una respuesta simple de una llamada API HTTP, y define dos propiedades: Idy Name.

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

C#

Use JsonConvert.DeserializeObject<dynamic>()para deserializar esta cadena en un tipo dinámico y luego simplemente acceda a sus propiedades de la manera habitual.

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

Si especifica el tipo de resultsvariable como dynamic, en lugar de utilizar la varpalabra clave, los valores de las propiedades se deserializarán correctamente, por ejemplo, Ida an inty no a JValue(gracias a GFoley83 por el comentario a continuación).

Nota : El enlace NuGet para el ensamblaje Newtonsoft es http://nuget.org/packages/newtonsoft.json .

Paquete : También puede agregar el paquete con el instalador nuget live, con su proyecto abierto, simplemente busque el paquete y luego simplemente instálelo, instale, desinstale, actualice , solo se agregará a su proyecto en Dependencias / NuGet

biofractal
fuente
Estaba usando el mismo código que el anterior para deserializar la respuesta de twitter con newtonsoft.dll versión 4.5.6 y estaba funcionando bien ... pero después de actualizarlo a la versión 5.0.6 ... comenzó a arrojar un error ... alguna idea por qué ??
Pranav
1
Bueno para objetos dinámicos, cuando sabemos o tenemos una clase de C # para poder consumir como una clase de C # en el reemplazo de dinámica, por ejemplo, <Myclass>.
MSTdev
2
Use dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json);aquí FTW. Se deserializará correctamente Ida int y no a JValue. Ver aquí: dotnetfiddle.net/b0WxGJ
GFoley83
@biofractal ¿Cómo haría esto dynamic results = JsonConvert.DeserializeObject<dynamic>(json); en VB.NET? Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json)No funciona.
Flo
41

Con la dynamicpalabra clave, se vuelve realmente fácil analizar cualquier objeto de este tipo:

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
fuente
Quería saber cómo recorrer los resultados y esto tardó demasiado en encontrar ... ¡gracias!
batoutofhell
22

Corríjame si me equivoco, pero creo que el ejemplo anterior está ligeramente fuera de sincronización con la última versión de la biblioteca Json.NET de James Newton.

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];
Rick Leitch
fuente
1
gracias por su respuesta Rick, ya que se parece a los ejemplos que encontré en la documentación más reciente también.
J Benjamin
1
Sí, dado que Arcain corrigió el error tipográfico, mi comentario ahora se ve meticuloso: '(. Originalmente publiqué porque no reconocí a JToken.Parse.
Rick Leitch el
1
No es nada quisquilloso: definitivamente hubo un error, y siempre hay más de una forma de hacerlo. Por cierto, mi versión de Json.NET admite la sintaxis usando el indexador activado JObject, pero el código que modifiqué para mi respuesta se extrajo del código haciendo uso de una sobrecarga del SelectTokenmétodo para poder suprimir excepciones si el token no estaba encontrado: JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch)así que de ahí proviene la verbosidad.
arcain
18

Si, como yo, prefiere tratar con objetos fuertemente tipados **, vaya con:

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

De esta manera, puede utilizar la comprobación de errores de tipo intellisense y tiempo de compilación.

Puede crear fácilmente los objetos requeridos copiando su JSON en la memoria y pegándolo como objetos JSON (Visual Studio -> Editar -> Pegado especial -> Pegar JSON como clases).

Vea aquí si no tiene esa opción en Visual Studio.

También deberá asegurarse de que su JSON sea válido. Agregue su propio objeto al principio si es solo una matriz de objetos. es decir, { "obj": [{}, {}, {}]}

** Sé que la dinámica hace las cosas más fáciles a veces, pero estoy un poco viejo con esto.

Guy Lowe
fuente
1
Mucho mi preferencia se acercó a la programación. Me gustan los objetos mecanografiados fuertes. Gracias por usar y modificar este código.
j.hull
11

Lista dinámica escrita libremente: deserialice y lea los valores

// 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
fuente
8

Me gusta este metodo:

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

Ahora puede acceder a cualquier cosa que desee utilizando dictObjcomo diccionario. También puede usarlo Dictionary<string, string>si prefiere obtener los valores como cadenas.

Puede usar este mismo método para emitir como cualquier tipo de objeto .NET.

Blairg23
fuente
2
Este método me parece muy bueno por dos razones: 1) cuando no le importa el tipo de datos (todo es una cadena) y 2) es conveniente trabajar con un diccionario de valores
netfed
7

Además, si solo está buscando un valor específico anidado dentro del contenido JSON, puede hacer algo así:

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

Y así sucesivamente desde allí.

Esto podría ayudar si no desea asumir el costo de convertir todo el JSON en un objeto C #.

Tony
fuente
2

Creé un Extionclass para 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); }
}

Patrón de diseño:

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

Uso:

   Myobject dd= Myobject.GetObject(jsonstring);

                 Console.WriteLine(dd.prop1);
Sloomy
fuente
1

Bastante tarde para esta fiesta, pero hoy me encontré con este problema en el trabajo. Así es como resolví el problema.

Estaba accediendo a una API de terceros para recuperar una lista de libros. El objeto devolvió un objeto JSON masivo que contenía aproximadamente más de 20 campos, de los cuales solo necesitaba la ID como un objeto de cadena de lista. Usé linq en el objeto dinámico para recuperar el campo específico que necesitaba y luego lo inserté en mi objeto de cadena Lista.

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
fuente
0

Finalmente obtenga el nombre del estado de JSON

¡Gracias!

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
fuente