- PVSM.RU - https://www.pvsm.ru -
Если Вы читаете данную статью, значит, скорее всего, Вы в курсе что такое JSON и картинка ниже Вам знакома. Но в любом случае советую посетить эту страничку [1], если Вы там еще не были, а так же перед прочтением желательно ознакомиться с общими принципами работы с протоколом JSON на языке C#, например по этой ссылке [2].
Хочу отметить, что данная заметка не претендует на какую-то полноту раскрытия темы. Цель данного текста – структурировать и сохранить те наработки, которые я использовал при работе с библиотекой Newtonsoft.Json.
По большому счету, в рамках статьи не так важно каким образом были получены исходные данные, для парсинга, однако данное пояснение наверняка облегчит восприятие материала. Итак, основная задача состоит в том, чтобы реализовать функции API криптобиржи EXMO [3]. Для простоты будем в основном работать с Публичным интерфейсом (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 [4]. Если загрузить эти данные в JSON C# Class Generator [5], нам будет предложена следующая структура класса для десериализации:
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 — неупорядоченный набор пар ключ/значение. Объект начинается с '{' (открывающей фигурной скобки) и заканчивается '}' (закрывающей фигурной скобкой). Каждое имя сопровождается: (двоеточием), пары ключ/значение разделяются запятой.
Из структуры, предложенной нам ранее 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.
Далее жмем «Обзор» («Browse») и в строке поиска вводим newtosoft.json. Ставим галочку напротив нашего решения и нажимаем «Установить» («Install»).
Пространство имен newtosoft.json.linq, после установки библиотеки, становится доступно для подключения к проекту:
using Newtonsoft.Json.Linq;
Для представления данных в newtosoft.json.linq используется абстрактный класс JToken [6], от которого наследуются классы JValue [7] (для представления простых значений) и JContainer [8] (для представления структур). Структуры в свою очередь могут представлять из себя JArray [9] (массив), JConstructor [10] (конструктор), JObject [11] (объект), либо JProperty [12] (свойство).
В классах 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 — упорядоченная коллекция значений. Массив начинается с '[' (открывающей квадратной скобки) и заканчивается ']' (закрывающей квадратной скобкой). Значения разделены запятой.
Как выглядит массив JSON можно увидеть пройдя по ссылке https://api.exmo.com/v1/currency [13]. Если попытаться преобразовать его в объект 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, значение может быть строкой в двойных кавычках, числом, true, false, null, объектом или массивом. Эти структуры могут быть вложенными. В реализации newotnsoft.json значением могут быть только объекты простых типов. Попытка преобразовать объект JArray в объект JValue приведет к исключению «System.InvalidCastException».
В качестве ознакомительного материала по работе с конкретными значениями (класс JValue) в библиотеке newtonsoft.json — хорошо подойдут примеры из документации раз [14] и два [15].
Пример использования для данных с биржи:
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>>>();
}
}
}
Наиболее полную и подробную информацию о библиотеке всегда можно почерпнуть из официальной документации [16].
Автор: Дмитрий Куликов
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/340862
Ссылки в тексте:
[1] страничку: https://www.json.org/json-ru
[2] по этой ссылке: https://habr.com/ru/post/176087/
[3] EXMO: https://exmo.me/ru/api
[4] https://api.exmo.com/v1/trades/?pair=BTC_USD: https://api.exmo.com/v1/trades/?pair=BTC_USD
[5] JSON C# Class Generator: http://json2csharp.com/
[6] JToken: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JToken.htm
[7] JValue: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JValue.htm
[8] JContainer: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JContainer.htm
[9] JArray: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JArray.htm
[10] JConstructor: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JConstructor.htm
[11] JObject: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JObject.htm
[12] JProperty: https://www.newtonsoft.com/JSON/help/html/T_Newtonsoft_Json_Linq_JProperty.htm
[13] https://api.exmo.com/v1/currency: https://api.exmo.com/v1/currency
[14] раз: https://www.newtonsoft.com/JSON/help/html/JValueCast.htm
[15] два: https://www.newtonsoft.com/JSON/help/html/JValueValue.htm
[16] официальной документации: https://www.newtonsoft.com/JSON/help/html/Introduction.htm
[17] Источник: https://habr.com/ru/post/481514/?utm_source=habrahabr&utm_medium=rss&utm_campaign=481514
Нажмите здесь для печати.