Работа с библиотекой Newtonsoft.Json на реальном примере. Часть 1

от автора

Если Вы читаете данную статью, значит, скорее всего, Вы в курсе что такое JSON и картинка ниже Вам знакома. Но в любом случае советую посетить эту страничку, если Вы там еще не были, а так же перед прочтением желательно ознакомиться с общими принципами работы с протоколом JSON на языке C#, например по этой ссылке.



Хочу отметить, что данная заметка не претендует на какую-то полноту раскрытия темы. Цель данного текста – структурировать и сохранить те наработки, которые я использовал при работе с библиотекой Newtonsoft.Json.

Постановка задачи

По большому счету, в рамках статьи не так важно каким образом были получены исходные данные, для парсинга, однако данное пояснение наверняка облегчит восприятие материала. Итак, основная задача состоит в том, чтобы реализовать функции API криптобиржи EXMO. Для простоты будем в основном работать с Публичным интерфейсом (Public API). В данном случае запрос информации будет выглядеть как обычный адрес странички сайта. Для отправки запросов используется класс WebClient пространства имен System.Net:

var wb = new WebClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD"; string answer = wb.DownloadString(request); 

Результат работы данной программы можно увидеть пройдя по ссылке https://api.exmo.com/v1/trades/?pair=BTC_USD. Если загрузить эти данные в JSON C# Class Generator, нам будет предложена следующая структура класса для десериализации:

    public class BTCUSD     {         public int trade_id { get; set; }         public string type { get; set; }         public string quantity { get; set; }         public string price { get; set; }         public string amount { get; set; }         public int date { get; set; }     }     public class RootObject     {         public List<BTCUSD> BTC_USD { get; set; }     } 

Надо отметить, что сама функция “trades” криптобиржи позволяет запрашивать информацию сразу по нескольким валютным парам. И в случае запроса

string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD";

Структура класса будет выглядеть уже следующим образом:

    public class BTCUSD     {         public int trade_id { get; set; }         public string type { get; set; }         public string quantity { get; set; }         public string price { get; set; }         public string amount { get; set; }         public int date { get; set; }     }     public class ETHUSD     {         public int trade_id { get; set; }         public string type { get; set; }         public string quantity { get; set; }         public string price { get; set; }         public string amount { get; set; }         public int date { get; set; }     }     public class RootObject     {         public List<BTCUSD> BTC_USD { get; set; }         public List<ETHUSD> ETH_USD { get; set; }     } 

Очевидно, что для каждой комбинации валютных пар классы не подготовишь, а значит нужно как-то выкручиваться.

Как делать не нужно.

Удивительно, но хороших статей по работе с протоколом JSON в русскоязычном интернете найти практически невозможно. Как следствие, те реализации EXMO API, которые можно найти на GitHub`е так или иначе содержат манипуляции с исходной структурой данных с использованием spit`ов, trim`ов и тому подобных непотребств.

Надо признать, что я то же сначала изобрел велосипед и реализовал парсинг самостоятельно. Несмотря на то, что код абсолютно рабочий, это отличный пример как НЕЛЬЗЯ делать.

LinkedList<OrderInform> loi = new LinkedList<OrderInform>(); try {   string[] json = answer.Split(new char[] { '{', '}' });   foreach (var item in json)   {     if (item.Length > 20)     {       string[] trade = item.Split(new char[] { ',' });       if (trade.Length > 5)       {         string t_id = trade[0].Split(new char[] { ':' })[1].Trim('"');         string t_type = trade[1].Split(new char[] { ':' })[1].Trim('"');         string t_quantity = trade[2].Split(new char[] { ':' })[1].Trim('"')           .Replace(".", ",");         string t_price = trade[3].Split(new char[] { ':' })[1].Trim('"')           .Replace(".", ",");         string t_amount = trade[4].Split(new char[] { ':' })[1].Trim('"')           .Replace(".", ",");         string t_date_time = trade[5].Split(new char[] { ':' })[1].Trim('"');         loi.AddLast(new OrderInform(pair, Convert.ToInt32(t_id), t_type,           Convert.ToDouble(t_quantity), Convert.ToDouble(t_price),            Convert.ToDouble(t_amount),           (new DateTime(1970, 1, 1, 0, 0, 0, 0))           .AddSeconds(Convert.ToInt32(t_date_time))));       }     }   }   return loi; }  catch (Exception ex) {   return new LinkedList<OrderInform>(); } 

Объекты JSON (класс JObject)

Объект JSON — неупорядоченный набор пар ключ/значение. Объект начинается с ‘{‘ (открывающей фигурной скобки) и заканчивается ‘}’ (закрывающей фигурной скобкой). Каждое имя сопровождается: (двоеточием), пары ключ/значение разделяются запятой.

Из структуры, предложенной нам ранее JSON C# Class Generator`ом видно, что для каждой валютной пары у нас есть 6 постоянных полей. Обернем их в отдельный класс и назовем его Order.

    class Order     {         public int trade_id { get; set; }         public string type { get; set; }         public double quantity { get; set; }         public double price { get; set; }         public double amount { get; set; }         public int date { get; set; }     } 

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

Для работы с библиотекой ее необходимо установить.

Чтобы установить библиотеку в Visual Studio можно просто выполнить поиск в NuGet.
Нажимаем правой кнопкой мыши на Решение (Solution) в Обозревателе решений (Solution explorer) и выбираем пункт «Управлением пакетами NuGet для решения…» («Mange NuGet Packages for solution…»).

Далее жмем «Обзор» («Browse») и в строке поиска вводим newtosoft.json. Ставим галочку напротив нашего решения и нажимаем «Установить» («Install»).

Пространство имен newtosoft.json.linq, после установки библиотеки, становится доступно для подключения к проекту:

using Newtonsoft.Json.Linq; 

Для представления данных в newtosoft.json.linq используется абстрактный класс JToken, от которого наследуются классы JValue (для представления простых значений) и JContainer (для представления структур). Структуры в свою очередь могут представлять из себя JArray (массив), JConstructor (конструктор), JObject (объект), либо JProperty (свойство).

В классах JArray и JObject имеется метод Parse, позволяющей преобразовывать JSON данные из обычной строки в соответствующие объекты. Так, если мы воспользуемся методом JObject.Parse(String) для структуры данных функции trades биржи Exmo:

var wb = new WebClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string answer = wb.DownloadString(request);  JObject jObject = JObject.Parse(answer); 

мы увидим структуру из двух элементов, каждый из которых представляет пару ключ-значение. Ключом в данном случае будет название валютной пары (тип String), а значением — список сделок (тип JToken):

Теперь списки сделок нам доступны по ключам «BTC_USD» и «USD_ETH». Например сделки по валютной паре «BTC_USD»:

Далее нам только останется перевести полученные данные в удобный для работы тип, например List<Order>, используя метод ToObject<>():

var wb = new WebClient(); string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; string answer = wb.DownloadString(request); JObject jObject = JObject.Parse(answer);  JToken list = jObject["BTC_USD"]; List<Order> trades = list.ToObject<List<Order>>(); 

либо всю структуру преобразовать в тип Dictionary<string, List<Order>>:

string request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; var wb = new WebClient(); string answer = wb.DownloadString(request); JObject jObject = JObject.Parse(answer);  Dictionary<string, List<Order>> trades = new Dictionary<string, List<Order>>(); foreach (KeyValuePair<string, JToken> obj in jObject)   trades.Add(obj.Key, obj.Value.ToObject<List<Order>>()); 

Массивы JSON (класс JArray)

Массив JSON — упорядоченная коллекция значений. Массив начинается с ‘[‘ (открывающей квадратной скобки) и заканчивается ‘]’ (закрывающей квадратной скобкой). Значения разделены запятой.

Как выглядит массив JSON можно увидеть пройдя по ссылке https://api.exmo.com/v1/currency. Если попытаться преобразовать его в объект JObject, так как мы делали с предыдущим запросом:

var wb = new WebClient(); string request = "https://api.exmo.com/v1/currency"; string answer = wb.DownloadString(request);  JObject jObject = JObject.Parse(answer); 

мы получим исключение Newtonsoft.Json.JsonReaderException, сообщающее нам, что преобразуемые данные не являются объектом.

Массивы должны быть преобразованы в специальный тип JArray, который, так же как JObject, наследуется от JContainer.

var wb = new WebClient(); string request = "https://api.exmo.com/v1/currency"; string answer = wb.DownloadString(request);  JArray jArray = JArray.Parse(answer); 

В случае, когда при парсинге, исходная структура заранее неизвестна, либо требуется возможность обработки обоих типов данных — в качестве типа преобразуемого объекта можно указать JContainer, или же JToken, тогда во время парсинга данные будут неявно преобразованы к нужному типу.

var wb = new WebClient();  string request = "https://api.exmo.com/v1/currency"; string answer = wb.DownloadString(request); JToken jArray = JToken.Parse(answer); //  Неявное преобразование в JArray List<string> list = jArray.ToObject<List<string>>();  request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD"; answer = wb.DownloadString(request); JToken jObject = JToken.Parse(answer); //  Неявное преобразование в JObject Dictionary<string, List<Order>> dict =      jObject.ToObject<Dictionary<string, List<Order>>>(); 

Значения JSON (класс JValue)

Согласно описанию формата JSON, значение может быть строкой в двойных кавычках, числом, true, false, null, объектом или массивом. Эти структуры могут быть вложенными. В реализации newotnsoft.json значением могут быть только объекты простых типов. Попытка преобразовать объект JArray в объект JValue приведет к исключению «System.InvalidCastException».

В качестве ознакомительного материала по работе с конкретными значениями (класс JValue) в библиотеке newtonsoft.json — хорошо подойдут примеры из документации раз и два.

Пример использования для данных с биржи:

             var wb = new WebClient();  string request = "https://api.exmo.com/v1/currency"; string answer = wb.DownloadString(request); JArray jArray = JArray.Parse(answer); foreach (JValue value in jArray) {     string s = (string)value; } 

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

Полный текст программы.

using System.Collections.Generic; using System.Net; using Newtonsoft.Json; using Newtonsoft.Json.Linq;  namespace JSONObjects {     class Order     {         public int trade_id { get; set; }         public string type { get; set; }         public double quantity { get; set; }         public double price { get; set; }         public double amount { get; set; }         public int date { get; set; }     }     class Program     {         static void Main(string[] args)         {             var wb = new WebClient();              string request = "https://api.exmo.com/v1/currency";             string answer = wb.DownloadString(request);             JToken jArray = JToken.Parse(answer);             //  Неявное преобразование в JArray             List<string> list = jArray.ToObject<List<string>>();              request = "https://api.exmo.com/v1/trades/?pair=BTC_USD,ETH_USD";             answer = wb.DownloadString(request);             JToken jObject = JToken.Parse(answer);             //  Неявное преобразование в JObject             Dictionary<string, List<Order>> dict =                 jObject.ToObject<Dictionary<string, List<Order>>>();         }     } } 

Наиболее полную и подробную информацию о библиотеке всегда можно почерпнуть из официальной документации.


ссылка на оригинал статьи https://habr.com/ru/post/481514/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *