Deserializowanie JSON do obiektu .NET przy użyciu Newtonsoft (lub LINQ do JSON?)

318

Wiem, że jest kilka postów na temat Newtonsoft, więc mam nadzieję, że to nie jest dokładnie powtórzenie ... Próbuję przekonwertować dane JSON zwrócone przez API Kazaa na ładny obiekt pewnego rodzaju

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

Ta linia JsonConvert jest najnowszą wersją, której próbowałem ... Nie do końca rozumiem i miałem nadzieję wyeliminować trochę pracy, pytając was. Początkowo próbowałem przekonwertować go na słownik lub coś w tym stylu ... i tak naprawdę muszę po prostu uchwycić kilka wartości, więc sądząc z dokumentacji, może LINQ do JSON firmy Newtonsoft może być lepszym wyborem? Myśli / Linki?

Oto przykład danych zwrotnych 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
}

Przeczytałem jeszcze trochę i odkryłem, że LINQ do JSON firmy Newtonsoft jest dokładnie tym, czego chciałem ... używając WebClient, Stream, StreamReader i Newtonsoft ... Mogę trafić Kazaa dla danych JSON, wyodrębnić adres URL, pobrać plik i zrobić to wszystko w jak siedem linii kodu! Kocham to.

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

Ten post zawiera tak wiele trafień, że pomyślałem, że pomocne może być uwzględnienie bitów „używania” omówionych w komentarzach.

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
źródło
6
Zręczny przykład, dzięki. Tylko sugestia: może masz po lewej stronie tej off dla zwięzłości, ale ponieważ WebClient, Streami StreamReaderwszystko wdrożenia IDisposable, warto dodać kilka usingbloków kodu.
arcain
ah tak, dobra rozmowa ... (tak naprawdę to była tylko aplikacja na konsolę. Pracowałem naprawdę szybko, aby poszukać zadań, które zamierzam wymyślić). Teraz przejdź do badania ostatniego fragmentu układanki, szyfrowania HLS + AES :) ugh ... lol
J Benjamin
1
+1 Dziękujemy za opublikowanie przykładu Linq. Dokładnie to, czego potrzebowałem.
Mark Wilkins,
Czy rozwiązanie newtonsoft również nie dokonuje w pełni deserializacji JSON? Podobnie jak rozwiązanie @ arcain.
AXMIM
Zwróć uwagę na link tutaj: LINQ do JSON
yu yang Jian

Odpowiedzi:

259

Jeśli potrzebujesz tylko pobrać kilka elementów z obiektu JSON, użyłbym LINQ Json.NET do JObjectklasy JSON . Na przykład:

JToken token = JObject.Parse(stringFullOfJson);

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

Podoba mi się to podejście, ponieważ nie trzeba całkowicie deserializować obiektu JSON. Jest to przydatne w przypadku interfejsów API, które mogą czasami zaskakiwać brakującymi właściwościami obiektów, takimi jak Twitter.

Dokumentacja: Serializacja i deserializacja JSON z Json.NET i LINQ do JSON z Json.NET

arcain
źródło
1
ya Właściwie to zrobiłem trochę więcej czytania i testowania ... okazało się, że jest to również dobry sposób na zrobienie tego ... Newtonsoft, całkiem niezła biblioteka, opublikuję mój przykład dla innych
J Benjamin
1
opublikowałem szorstki przykład tego, jak to robiłem ... niezupełnie tak samo, widzę, że zasugerowałeś JToken.Parse ... nie jestem pewien różnic między nimi, ale tak, dobre rzeczy!
J Benjamin,
1
@Jbenjamin Thanks! To była literówka. JToken jest klasą podstawową dla JObject, a moją osobistą preferencją jest praca z bardziej abstrakcyjnym typem. Dziękuję za zwrócenie na to mojej uwagi.
arcain
Przepraszam, ale czy powinien to być JToken, czy JObject? Powyższy kod wciąż wyświetla błąd „Błąd odczytu JObject z JsonReader” co jakiś czas.
TYRONEMICHAEL
1
@Tyrone Pewnie, nie ma problemu. Właściwie używam tego kodu również do analizowania statusu Twittera i musiałem napisać sporo błędów obsługi w połączeniach do Twittera, ponieważ czasami mogą być nierówne. Jeśli jeszcze tego nie robisz, zalecam zrzucenie surowej odpowiedzi JSON z Twittera do dziennika przed próbą jej przeanalizowania. Następnie, jeśli się nie powiedzie, możesz przynajmniej sprawdzić, czy dostałeś coś fajnego przez drut.
arcain
272

Możesz użyć typu C #, dynamicaby ułatwić. Ta technika upraszcza również faktoring, ponieważ nie polega na magicznych ciągach.

JSON

Poniższy ciąg JSON jest prostą odpowiedzią z wywołania API HTTP i definiuje dwie właściwości: Idi Name.

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

DO#

Użyj, JsonConvert.DeserializeObject<dynamic>()aby przekształcić ciąg z postaci szeregowej w postać dynamiczną, a następnie po prostu uzyskaj dostęp do jego właściwości w zwykły sposób.

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

Jeśli określisz typ resultszmiennej jako dynamic, zamiast zamiast varsłowa kluczowego, wówczas wartości właściwości będą poprawnie deserializować, np. IdDo a, inta nie a JValue(dzięki GFoley83 za komentarz poniżej).

Uwaga : Łącze NuGet dla zestawu Newtonsoft to http://nuget.org/packages/newtonsoft.json .

Pakiet : Możesz także dodać pakiet za pomocą instalatora na żywo Nuget, po otwarciu projektu wystarczy przejrzeć pakiet, a następnie zainstalować, zainstalować, odinstalować, zaktualizować , zostanie on dodany do projektu w sekcji Zależności / NuGet

biofraktal
źródło
Użyłem tego samego fragmentu kodu, jak powyżej, do deserializacji odpowiedzi Twittera za pomocą newtonsoft.dll w wersji 4.5.6 i działało dobrze .. ale po aktualizacji do wersji 5.0.6 .. zaczął zgłaszać błąd ... jakikolwiek pomysł czemu ??
Pranav,
1
Dobre dla obiektu dynamicznego, gdy wiemy lub mamy klasę C #, więc możemy zużywać jako klasę C # po zamianie dynamiki np. <Myclass>.
MSTdev
2
Użyj dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json);tutaj FTW. Poprawnie deserializuje Iddo int, a nie do JValue. Zobacz tutaj: dotnetfiddle.net/b0WxGJ
GFoley83
@biofractal Jak mam to zrobić dynamic results = JsonConvert.DeserializeObject<dynamic>(json); w VB.NET? Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json)nie działa.
Flo
41

Dzięki dynamicsłowu kluczowemu bardzo łatwo jest przeanalizować dowolny obiekt tego rodzaju:

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
źródło
Chciałem wiedzieć, jak przeglądać wyniki, a znalezienie tego zajęło zbyt wiele czasu ... dzięki!
batoutofhell
22

Popraw mnie, jeśli się mylę, ale uważam, że poprzedni przykład jest nieco zsynchronizowany z najnowszą wersją biblioteki Json.NET Jamesa Newtona.

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];
Rick Leitch
źródło
1
dziękuję za twoją odpowiedź. Rick, również wygląda podobnie do przykładów, które znalazłem w najnowszej dokumentacji.
J Benjamin
1
Tak, odkąd Arcain naprawił literówkę, mój komentarz wygląda teraz głupkowato: „(Oryginalnie napisałem, ponieważ nie rozpoznałem JToken.Parse.
Rick Leitch
1
Wcale nie podstępny - na pewno był błąd i zawsze jest więcej niż jeden sposób, aby to zrobić. Nawiasem mówiąc, moja wersja Json.NET obsługuje składnię przy włączonym indeksatorze JObject, ale kod, który zmodyfikowałem dla mojej odpowiedzi, został pobrany z kodu wykorzystującego przeciążenie SelectTokenmetody, dzięki czemu mogłem pominąć wyjątki, jeśli token nie był znaleziono JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch):, stąd pochodzi gadatliwość.
arcain
18

Jeśli, podobnie jak ja, wolisz zajmować się silnie typowanymi obiektami **, przejdź do:

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

W ten sposób możesz użyć intellisense i skompilować sprawdzanie błędów typu czasowego.

Możesz łatwo utworzyć wymagane obiekty, kopiując JSON do pamięci i wklejając go jako obiekty JSON (Visual Studio -> Edycja -> Wklej specjalnie -> Wklej JSON jako klasy).

Zobacz tutaj, jeśli nie masz tej opcji w Visual Studio.

Musisz także upewnić się, że JSON jest ważny. Dodaj swój obiekt na początku, jeśli jest to tylko tablica obiektów. tj. { "obj": [{}, {}, {}]}

** Wiem, że dynamika sprawia, że ​​czasem łatwiej, ale jestem z tego trochę olakool.

Guy Lowe
źródło
1
Bardzo podobało mi się podejście do programowania. Lubię silnie pisane przedmioty. Dzięki, ponieważ użyłem i zmodyfikowałem ten kod.
j
11

Lista dynamiczna luźno wpisana - deserializuj i czytaj wartości

// 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
źródło
8

Podoba mi się ta metoda:

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

Możesz teraz uzyskać dostęp do wszystkiego, co chcesz, korzystając ze dictObjsłownika. Możesz także użyć, Dictionary<string, string>jeśli wolisz, aby wartości były ciągami.

Tej samej metody można użyć do rzutowania jak do dowolnego obiektu .NET.

Blairg23
źródło
2
Uważam tę metodę za bardzo przyjemną z dwóch powodów: 1) gdy nie zależy ci na typie danych (wszystko jest ciągiem), i 2) wygodnie jest pracować ze słownikiem wartości
netfed
7

Ponadto, jeśli szukasz konkretnej wartości zagnieżdżonej w treści JSON, możesz zrobić coś takiego:

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

I tak dalej.

Może to pomóc, jeśli nie chcesz ponosić kosztów konwersji całego JSON na obiekt C #.

Tony
źródło
2

stworzyłem Extionclass dla 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); }
}

Wzór:

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

Stosowanie:

   Myobject dd= Myobject.GetObject(jsonstring);

                 Console.WriteLine(dd.prop1);
Ponury
źródło
1

Dość późno na tę imprezę, ale sam spotkałem się z tym problemem w pracy. Oto jak rozwiązałem problem.

Uzyskiwałem dostęp do interfejsu API innej firmy, aby pobrać listę książek. Obiekt zwrócił ogromny obiekt JSON zawierający około 20+ pól, z których potrzebowałem tylko identyfikatora jako obiektu łańcucha List. Użyłem linq na obiekcie dynamicznym, aby pobrać potrzebne pole, a następnie wstawiłem je do mojego ciągu znaków 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
źródło
0

Wreszcie uzyskaj nazwę stanu od JSON

Dziękuję Ci!

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
źródło