
Хочу отметить, что данная заметка не претендует на какую-то полноту раскрытия темы. Цель данного текста – структурировать и сохранить те наработки, которые я использовал при работе с библиотекой 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; } }
Очевидно, что для каждой комбинации валютных пар классы не подготовишь, а значит нужно как-то выкручиваться.
Надо признать, что я то же сначала изобрел велосипед и реализовал парсинг самостоятельно. Несмотря на то, что код абсолютно рабочий, это отличный пример как НЕЛЬЗЯ делать.
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.
Нажимаем правой кнопкой мыши на Решение (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»:

Далее нам только останется перевести полученные данные в удобный для работы тип, например
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>>();
либо всю структуру преобразовать в тип
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/
Добавить комментарий