Desserializando o objeto JSON para .NET usando Newtonsoft (ou LINQ para JSON, talvez?)

318

Eu sei que existem alguns posts sobre a Newtonsoft, então espero que isso não seja exatamente uma repetição ... Estou tentando converter dados JSON retornados pela API do Kazaa em um bom objeto de algum 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();

Essa linha JsonConvert é apenas a mais recente que eu estava tentando ... Eu não estou entendendo direito e esperava eliminar algum trabalho de pés, perguntando a vocês. Eu estava originalmente tentando convertê-lo em um dicionário ou algo assim ... e, na verdade, eu só preciso entender alguns valores lá, a fim de julgar pela documentação, talvez o LINQ to JSON da Newtonsoft possa ser uma escolha melhor? Pensamentos / Links?

Aqui está um exemplo dos dados 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
}

Eu li um pouco mais e descobri que o LINQ to JSON da Newtonsoft é exatamente o que eu queria ... usando WebClient, Stream, StreamReader e Newtonsoft ... Eu posso acessar o Kazaa para obter dados JSON, extrair uma URL, baixar o arquivo e fazer isso tudo em sete linhas de código! Eu amo isso.

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

Este post recebe tantos acessos que achei que poderia ser útil incluir os bits "using" discutidos nos comentários.

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
fonte
6
Exemplo liso, obrigado. Apenas uma sugestão: você pode ter deixado isso por breves instantes, mas WebClient, Streamcomo StreamReadertodas as implementações IDisposable, você pode adicionar alguns usingblocos ao seu código.
precisa saber é
ah sim, boa ligação ... (sim, na verdade, era apenas um aplicativo de console. Eu estava correndo muito rápido para pesquisar as tarefas que estavam chegando) Agora, para pesquisar a última peça do quebra-cabeça, a criptografia HLS + AES :) ugh ... lol
J Benjamin
1
+1 Obrigado por postar o exemplo do Linq. Exatamente o que eu precisava.
Mark Wilkins
A solução da newtonsoft também não desserializa totalmente o JSON? Assim como a solução da @ arcain.
AXMIM
Observe o link aqui: LINQ para JSON
yu yang Jian

Respostas:

259

Se você apenas precisar obter alguns itens do objeto JSON, eu usaria a JObjectclasse LINQ to JSON do Json.NET . Por exemplo:

JToken token = JObject.Parse(stringFullOfJson);

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

Eu gosto dessa abordagem porque você não precisa desserializar completamente o objeto JSON. Isso é útil com APIs que às vezes podem surpreendê-lo com propriedades de objetos ausentes, como o Twitter.

Documentation: Serializando e desserializando JSON com Json.NET e LINQ to JSON com Json.NET

arcain
fonte
1
ya, na verdade, fiz um pouco mais de leitura e teste ... achei isso uma boa maneira de fazê-lo também ... Newtonsoft, biblioteca bastante legal, vou postar meu exemplo para outros
J Benjamin
1
postou um exemplo aproximado de como eu estava fazendo isso ... não exatamente o mesmo, vejo que você sugeriu o JToken.Parse ... não tenho certeza das diferenças entre os dois ainda, mas sim, coisas boas!
J Benjamin
1
@Jbenjamin Thanks! Isso foi um erro de digitação. JToken é a classe base para JObject, e é apenas minha preferência pessoal trabalhar com o tipo mais abstrato. Obrigado por chamar minha atenção.
Arcain
Desculpe, mas deveria ser JToken ou JObject? O código acima ainda está lançando o erro "Erro ao ler JObject do JsonReader" de vez em quando.
TYRONEMICHAEL
1
@Tyrone Claro, não há problema. Na verdade, eu também uso esse código para analisar o status do Twitter e tive que escrever um pouco de manipulação de erros nas chamadas para o Twitter, pois elas podem ser irregulares às vezes. Se você ainda não estiver fazendo isso, recomendo despejar a resposta JSON bruta do Twitter em um log antes de tentar analisá-la. Então, se falhar, você pode pelo menos ver se recebeu algo estranho por cima do fio.
arcain 03/02/2012
272

Você pode usar o dynamictipo C # para facilitar as coisas. Essa técnica também simplifica a re-fatoração, pois não depende de seqüências de caracteres mágicas.

JSON

A sequência JSON abaixo é uma resposta simples de uma chamada da API HTTP e define duas propriedades: Ide Name.

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

C #

Use JsonConvert.DeserializeObject<dynamic>()para desserializar essa sequência em um tipo dinâmico e simplesmente acessar suas propriedades da maneira usual.

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

Se você especificar o tipo da resultsvariável como dynamic, em vez de usar a varpalavra - chave, os valores da propriedade serão desserializados corretamente, por exemplo, Idpara um inte não para um JValue(obrigado a GFoley83 pelo comentário abaixo).

Nota : O link NuGet para a montagem da Newtonsoft é http://nuget.org/packages/newtonsoft.json .

Pacote : Você também pode adicionar o pacote com o instalador do nuget live, com o seu projeto aberto, basta procurar o pacote e instalar, instalar, desinstalar, atualizar ; ele será adicionado ao seu projeto em Dependências / NuGet

biofractal
fonte
Eu estava usando o mesmo trecho de código acima para desserializar a resposta do twitter com newtonsoft.dll versão 4.5.6 e estava funcionando bem ..mas depois de atualizá-lo para a versão 5.0.6 .. ele começou a gerar erro ... alguma idéia porque ??
Pranav
1
Bom para objeto dinâmico, quando conhecemos ou temos uma classe c # para que possamos consumir como uma classe C # na substituição de dinâmica, por exemplo, <Myclass>.
MSTdev 21/01
2
Use dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json);aqui o FTW. Deserializará corretamente Idpara um int e não para a JValue. Veja aqui: dotnetfiddle.net/b0WxGJ
GFoley83
@biofractal Como eu faria isso dynamic results = JsonConvert.DeserializeObject<dynamic>(json); no VB.NET? Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json)não funciona.
Flo
41

Com a dynamicpalavra-chave, fica muito fácil analisar qualquer objeto desse 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
fonte
Eu queria saber como percorrer os resultados e isso levou muito tempo para encontrar ... obrigado !!
batoutofhell
22

Corrija-me se estiver enganado, mas acredito que o exemplo anterior esteja ligeiramente fora de sincronia com a versão mais recente da biblioteca Json.NET de James Newton.

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];
Rick Leitch
fonte
1
obrigado pela sua resposta Rick, parecido com os exemplos que encontrei na documentação mais recente.
J Benjamin
1
Sim, desde Arcain fixa o erro de digitação, o meu comentário agora só olha nitpicky: '(I originalmente porque eu não reconheci JToken.Parse..
Rick Leitch
1
Nem um pouco exigente - definitivamente houve um erro e sempre há mais de uma maneira de fazê-lo. A propósito, minha versão do Json.NET suporta a sintaxe usando o indexador ativado JObject, mas o código que eu modifiquei para minha resposta foi extraído do código, fazendo uso de uma sobrecarga do SelectTokenmétodo, para que eu pudesse suprimir exceções se o token não fosse found JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch):, então é daí que veio a verbosidade.
precisa saber é
18

Se, como eu, você preferir lidar com objetos fortemente tipados **, vá com:

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

Dessa forma, você usa o intellisense e compila a verificação de erros do tipo de tempo.

Você pode criar facilmente os objetos necessários, copiando seu JSON na memória e colando-o como objetos JSON (Visual Studio -> Editar -> Colar Especial -> Colar JSON como Classes).

Veja aqui se você não possui essa opção no Visual Studio.

Você também precisará garantir que seu JSON seja válido. Adicione seu próprio objeto no início, se for apenas uma matriz de objetos. ie { "obj": [{}, {}, {}]}

** Eu sei que a dinâmica às vezes facilita as coisas, mas eu sou um pouco atrevida com isso.

Guy Lowe
fonte
1
Muito o meu preferido abordou a programação. Eu gosto de objetos digitados fortes. Obrigado, pois usei e modifiquei este código.
j.hull
11

Lista dinâmica digitada livremente - desserialize e leia os 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
fonte
8

Eu gosto deste método:

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

Agora você pode acessar o que quiser usando o dictObjcomo dicionário. Você também pode usar Dictionary<string, string>se preferir obter os valores como cadeias.

Você pode usar esse mesmo método para converter como qualquer tipo de objeto .NET.

Blairg23
fonte
2
I encontrar este método muito bom por dois motivos: 1) quando você não se preocupam com o tipo de dados (tudo é string), e 2) é conveniente para trabalhar com um dicionário de valores
netfed
7

Além disso, se você está apenas procurando por um valor específico aninhado no conteúdo JSON, pode fazer algo assim:

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

E assim por diante.

Isso pode ajudar se você não quiser arcar com o custo de converter todo o JSON em um objeto C #.

Tony
fonte
2

eu craited uma 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); }
}

Padrão de design:

 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
fonte
1

Bastante atrasado para esta festa, mas me deparei com essa questão hoje no trabalho. Aqui está como eu resolvi o problema.

Eu estava acessando uma API de terceiros para recuperar uma lista de livros. O objeto retornou um grande objeto JSON contendo aproximadamente 20 + campos, dos quais eu só precisava do ID como um objeto de sequência da lista. Eu usei linq no objeto dinâmico para recuperar o campo específico que eu precisava e depois o inseri no meu objeto de sequência de 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
fonte
0

Finalmente obtenha o nome do estado do JSON

Obrigado!

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
fonte