Десериализация JSON в объект .NET с помощью Newtonsoft (или LINQ to JSON, может быть?)

318

Я знаю, что есть несколько сообщений о Newtonsoft, так что, надеюсь, это не совсем повторение ... Я пытаюсь преобразовать данные JSON, возвращаемые API Kazaa, в какой-то хороший объект

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

Эта линия JsonConvert является самой последней из тех, что я пробовал ... Я не совсем понял, и надеялся избавиться от лишних усилий, спрашивая вас, ребята. Первоначально я пытался преобразовать его в словарь или что-то в этом роде ... и на самом деле мне просто нужно поймать там несколько значений, поэтому, судя по документации, может быть, LINTON to JSON от Newtonsoft может быть лучшим выбором? Мысли / ссылки?

Вот пример возвращаемых данных 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
}

Я еще немного прочитал и обнаружил, что LINQ to JSON от Newtonsoft - это именно то, что мне нужно ... используя WebClient, Stream, StreamReader и Newtonsoft ... Я могу нажать Kazaa для данных JSON, извлечь URL, загрузить файл и сделать это. все как семь строк кода! Я люблю это.

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

В этом посте так много обращений, что я думаю, что было бы полезно включить биты «использования», которые обсуждаются в комментариях.

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"]);
}
Дж Бенджамин
источник
6
Отличный пример, спасибо. Только предложение: вы , возможно, оставили это прочь для краткости, но с тех пор WebClient, Streamи StreamReaderвсе реализовать IDisposable, вы можете захотеть добавить некоторые usingблоки кода.
arcain
ах да, хороший вызов ... (да, это было на самом деле просто консольное приложение, которое я запустил очень быстро, чтобы найти задачи, которые у меня возникли) Теперь приступим к исследованию последнего кусочка головоломки, шифрования HLS + AES :) тьфу ... LOL
J Бенджамин
1
+1 Спасибо за публикацию примера Linq. Именно то, что мне было нужно.
Марк Уилкинс
Разве решение newtonsoft также не полностью десериализует JSON? Как и в случае решения @ arcain.
AXMIM
Обратите внимание на ссылку здесь: LINQ to JSON
yu yang Jian

Ответы:

259

Если вам просто нужно получить несколько элементов из объекта JSON, я бы использовал класс LINQ to JSON в Json.NET JObject. Например:

JToken token = JObject.Parse(stringFullOfJson);

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

Мне нравится этот подход, потому что вам не нужно полностью десериализовать объект JSON. Это очень удобно для API, которые иногда могут удивить вас отсутствующими свойствами объекта, такими как Twitter.

Документация: Сериализация и десериализация JSON с Json.NET и LINQ to JSON с Json.NET

arcain
источник
1
Я действительно немного больше читал и тестировал ... нашел, что это также хороший способ сделать это ... Newtonsoft, довольно хорошая библиотека, я опубликую свой пример для других
J Benjamin
1
опубликовал грубый пример того, как я это делал ... не совсем то же самое, я вижу, вы предложили JToken.Parse ... пока не уверен в различиях между двумя, но да, хорошие вещи!
J Бенджамин
1
@Jbenjamin Спасибо! Это была опечатка. JToken - это базовый класс для JObject, и я лично предпочитаю работать с более абстрактным типом. Спасибо, что обратили на это мое внимание.
arcain
Извините, но должен ли это быть JToken или JObject? Приведенный выше код все еще время от времени выдает ошибку «Ошибка чтения JObject из JsonReader».
TYRONEMICHAEL
1
@ Тайрон Конечно, нет проблем. Я на самом деле использую этот код и для разбора статуса в Твиттере, и мне пришлось написать немало ошибок, связанных с вызовами в Твиттере, так как они могут быть нечеткими. Если вы этого еще не сделали, я бы рекомендовал сбросить необработанный ответ JSON из Twitter в журнал, прежде чем пытаться его проанализировать. Затем, если это не удается, вы можете, по крайней мере, увидеть, если вы получили что-то напуганное по проводам.
Arcain
272

Вы можете использовать C # dynamic тип чтобы упростить задачу. Этот метод также упрощает ре-факторинг, поскольку он не зависит от магических струн.

JSON

Строка JSON ниже представляет собой простой ответ от вызова HTTP API и определяет два свойства: Idи Name.

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

C #

Используйте JsonConvert.DeserializeObject<dynamic>()для десериализации этой строки в динамический тип, а затем просто получите доступ к ее свойствам обычным способом.

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

Если вы укажете тип resultsпеременной как dynamicвместо varключевого слова, то значения свойств будут правильно десериализованы, например, Idв a, intа не a JValue(спасибо GFoley83 за комментарий ниже).

Примечание . Ссылка NuGet для сборки Newtonsoft: http://nuget.org/packages/newtonsoft.json .

Пакет : Вы также можете добавить пакет с помощью Nuget Live Installer, когда ваш проект открыт, просто просмотрите пакет, а затем просто установите его, установите, удалите, обновите , он будет просто добавлен в ваш проект в разделе Зависимости / NuGet.

biofractal
источник
Я использовал тот же кусок кода, что и выше, для десериализации ответа твиттера с помощью newtonsoft.dll версии 4.5.6, и он работал нормально ... но после обновления до версии 5.0.6 ... он начал выдавать ошибку ... любая идея Зачем ??
Пранав
1
Хорошо для динамического объекта, когда мы знаем или у нас есть класс C #, поэтому мы можем использовать его как класс C # при замене динамического, например, <Myclass>.
MSTdev
2
Используйте dynamic results = JsonConvert.DeserializeObject<ExpandoObject>(json);здесь FTW. Это будет правильно десериализовать Idв int, а не a JValue. Смотрите здесь: dotnetfiddle.net/b0WxGJ
GFoley83
@biofractal Как бы я сделал это dynamic results = JsonConvert.DeserializeObject<dynamic>(json); в VB.NET? Dim results As Object = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Object)(json)не работает.
Фло
@ Попробуй ... DeserializeObject (Of System.Dynamic.ExpandoObject) .
ToolmakerSteve
41

С помощью dynamicключевого слова становится действительно легко разобрать любой объект такого типа:

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;
}
Сушант Сривастава
источник
Я хотел знать, как просмотреть результаты, и это заняло слишком много времени, чтобы найти ... спасибо !!
batoutofhell
22

Поправьте меня, если я ошибаюсь, но предыдущий пример, я считаю, немного не синхронизирован с последней версией библиотеки Json.NET Джеймса Ньютона.

var o = JObject.Parse(stringFullOfJson);
var page = (int)o["page"];
var totalPages = (int)o["total_pages"];
Рик Лейтч
источник
1
спасибо за ваш ответ, Рик, да, это похоже на примеры, которые я нашел в самой последней документации.
J Бенджамин
1
Да, так как arcain исправил опечатку, мой комментарий теперь выглядит просто придирчиво: '(. Первоначально я писал, потому что не узнал JToken.Parse.
Рик Лейтч,
1
Совсем не придирчивый - определенно была ошибка, и всегда есть несколько способов сделать это. Кстати, моя версия Json.NET поддерживает синтаксис при включенном индексаторе JObject, но код, который я изменил для своего ответа, был взят из кода, использующего перегрузку SelectTokenметода, чтобы я мог подавлять исключения, если токен не был нашел JToken JToken.SelectToken(string tokenName, bool errorWhenNoMatch), так вот откуда пришло многословие.
Arcain
18

Если, как и я, вы предпочитаете иметь дело со строго типизированными объектами **, используйте:

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

Таким образом, вы можете использовать intellisense и проверку ошибок типа времени компиляции.

Вы можете легко создать необходимые объекты, скопировав свой JSON в память и вставив его как объекты JSON (Visual Studio -> Правка -> Специальная вставка -> Вставить JSON как классы).

Посмотрите здесь, если у вас нет этой опции в Visual Studio.

Вам также необходимо убедиться, что ваш JSON действителен. Добавьте свой собственный объект в начале, если это просто массив объектов. то есть { "obj": [{}, {}, {}]}

** Я знаю, что динамика иногда делает вещи проще, но я немного староват с этим.

Гай Лоу
источник
1
Очень мой любимый подход к программированию. Я люблю строго типизированные объекты. Спасибо за то, что я использовал и изменил этот код.
Дж.Халл
11

Динамический список Loosely Typed - десериализовать и прочитать значения

// 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;
}
Арун Прасад Е.С.
источник
8

Мне нравится этот метод:

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

Теперь вы можете получить доступ ко всему, что вы хотите, используя в dictObjкачестве словаря. Вы также можете использовать, Dictionary<string, string>если вы предпочитаете получать значения в виде строк.

Вы можете использовать этот же метод для приведения в качестве любого вида объекта .NET.

Blairg23
источник
2
Я нахожу этот метод очень хорошим по двум причинам: 1) когда вам нет дела до типа данных (все строки), и 2) удобно работать со словарем значений
netfed
7

Кроме того, если вы просто ищете конкретное значение, вложенное в контент JSON, вы можете сделать что-то вроде этого:

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

И так далее.

Это может помочь, если вы не хотите нести расходы на преобразование всего JSON в объект C #.

Тони
источник
2

я создал Extionclass для 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); }
}

Дизайн-шаблон:

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

Использование:

   Myobject dd= Myobject.GetObject(jsonstring);

                 Console.WriteLine(dd.prop1);
Sloomy
источник
1

Довольно поздно на эту вечеринку, но сегодня я столкнулся с этим вопросом на работе. Вот как я решил проблему.

Я получал доступ к стороннему API для получения списка книг. Объект возвратил массивный объект JSON, содержащий примерно 20+ полей, из которых мне нужен только идентификатор в качестве строкового объекта списка. Я использовал linq для динамического объекта, чтобы получить нужное поле, а затем вставил его в мой строковый объект списка.

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
источник
0

Наконец получить имя состояния из JSON

Спасибо!

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
источник