Аббревіатура JSON – це скорочення від англійських слів JavaScript Object Notation, тобто опис об’єктів в мові програмування JavaScript. Проте, опис об’єктів у цьому форматі получився настільки зручним, що його зараз використовують майже завжди, у випадках коли потрібно передати чи відправити кудись дані.
Щоб працювати з JSON у програмі ZennoPoster потрібно спочатку розібратись з тим, як саме описуються дані у цьому форматі. Нам може зустрітись текст який починається вусатою дужкою, і закінчується вусатою дужкою – це опис об’єкта. Всередині об’єкта можна зустріти поля у подвійних кавичках після яких іде двокрапка і значення. Поля розділені між собою комами. Також може зустрітись текст, який починається квадратною дужкою, і закінчується квадратною дужкою – це опис масиву. Масиви можуть находитись в якості значення поля об’єкта, або весь JSON текст може бути масивом. Всередині масивів находяться об’єкти, інші масиви і рядки. Елементи розділяються комами. Текстові рядки обрамлені просто подвійними кавичками. Зазвичай рядки знаходяться всередині масивів.
Більш детально з JSON можна ознайомитись у ВікіПедії.
Так от, щоб звертатись до об’єктів чи елементів масиву, які находяться в JSON, його потрібно десеріалізувати, тобто привести до такого об’єкта, з яким вміє працювати мова програмування на якій пишеться код. ZennoPoster дозволяє працювати використовуючи мову C#. Для роботи з JSON у C# є декілька способів, і один із них використання бібліотеки NewtonSoft.Json. Ця бібліотека використовується всередині ZennoPoster, тому встановлювати її додатково немає необхідності. Находиться вона у просторі імен Global.ZennoLab.Json. Говорю я про це тому, що ніхто спеціально не запам’ятовує всі специфічні команди для роботи з JSON. Коли є якась не стандартна ситуація, то потрібно просто піти на сайт розробників і прочитати необхідний розділ документації.
Коли в нас є JSON, він зберігається у текстовій змінній. Щоб його десеріалізувати і працювати з ним використовуючи макроси ZennoPoster у інших блоках використовується така операція:
project.Json.FromString(json);
Проте, коли у нас у перемінній буде текст не в форматі JSON, то результатом виконання буде помилка.
Інколи щоб уникнути такої поведінки, можна одіти інструкцію в блок try/catch
try { project.Json.FromString(json); } catch {return json; }
Може бути також протележна ситуація, коли у нас уже є десеріалізований JSON, який знаходиться у project.Json, і нам необхідно одержати JSON у вигляді текстового рядка. Тоді використовується інструкція:
return project.Json.ToString();
Проте, не завжди зручно користуватись саме таким способом, тому що в результаті такої десеріалізації у нас в об’єкті project.Json тип даних буде dynamic, який не завжди вказує на той тип, який може знадобитись. Тому, випадках, коли я знаю, що одержав об’єкт JSON – я використовую інструкцію:
var dic = Global.ZennoLab.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
Це дає можливість мені звертатись до полів по імені ключів словника:
return dic["field"];
Якщо я знаю що на вхід мені прийшов JSON масив – тоді я десеріалізую його у список словників такою інструкцією:
var list_dic = Global.ZennoLab.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(json);
Тоді, я зможу у звичайному циклі звернутись до кожного словника, або звертатись до них по індексу.
foreach(var dic in list_dic){ project.SendInfoToLog(string.Format("{0}", dic["field"])); }
Потрібно звернути увагу, що я в якості значеннь для словника вказую тип object, тому що там може бути як звичайний об’єкт, так і масив. Тому, у випадках, коли я знаю, що у мене якесь конкретне значення поля має тип даних масив – я просто серіалізую цей об’єкт в текстовий рядок, і наступною командою десеріалізую уже в потрібний мені тип даних словник чи список чи список словників.
foreach(var dic in list_dic){ string item1 = Global.ZennoLab.Json.JsonConvert.SerializeObject(dic["field_1"]); var dic_item = Global.ZennoLab.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(item1); string item2 = Global.ZennoLab.Json.JsonConvert.SerializeObject(dic["field_2"]); var array_item = Global.ZennoLab.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(item2); }
На цьому прикладі добре видно, чому не використовується стандартний project.Json.FromString – так як зразу після десеріалізації іншого текстового рядка – дані всередині project.Json зміняться, а значить перебрати всі значення за допомогою циклу не получиться.
Також, на цьому прикладі видно як можна серіалізувати будь-який об’єкт у текстовий рядок JSON, який наприклад можна добавити у список і вже відпрацьовувати у декілька потоків ZennoPoster.
JSON може бути у сформований у вигляді одного рядка, а може бути відформатованим для зручності перегляду людиною. Для цього у серіалізатор можна подати відповідні параметри:
return Global.ZennoLab.Json.JsonConvert.SerializeObject(ob, Global.ZennoLab.Json.Formatting.Indented); // з форматуванням return Global.ZennoLab.Json.JsonConvert.SerializeObject(ob, Global.ZennoLab.Json.Formatting.None); // в один рядок
І тут також виявляється що зручніше використовувати цю інструкцію, ніж project.Json.ToString(), так як в ньому немає можливості змінити форматування результату в потрібному вигляді.
Використовувати JSON у програмі ZennoPoster зручно також для того, щоб наприклад подивитись значення всіх змінних профіля, чи значеннь будь-яких об’єктів, не думаючи як саме їх потрібно приводити до текстового рядка (вивід в лог у ZennoPoster на відміну від Console.WriteLine приймає виключно текстові рядки).
Корисно також знати, що є можливість змінити налаштування серіалізації за змовчуванням на свої налаштування. Наприклад зазвичай при серіалізації поля які містять значення null можуть виключатись, і щоб добавляти їх, можна використовувати приблизно такі інструкції:
var settings = new Global.ZennoLab.Json.JsonSerializerSettings(); settings.NullValueHandling = Global.ZennoLab.Json.NullValueHandling.Include; return Global.ZennoLab.Json.JsonConvert.SerializeObject(ob, settings: settings );
У мові C# об’єкти передаються по посиланні. Це означає, що коли ми створили об’єкт, потім присвоїли його іншому об’єкту, після чого у початковому об’єкті змінили наприклад значення поля – то в іншому об’єкті також значення буде змінене. Для того, щоб змінити таку поведінку можна серіалізувати об’єкт у JSON, а потім десеріалізувати JSON у новий об’єкт. В результаті у нас буде дві незалежні копії об’єкта, у кожного об’єкта свої дані, зміна яких у одному об’єкті не буде впливати на інший об’єкт.
Інколи розробники замість того, щоб сформувати словник з даними і серіалізувати його у JSON лінуються, і беруть готовий текстовий рядок, в якому у необхідні поля пробують підставляти значення із змінних. Проте у такому випадку вони упускають один момент – всередині значення змінної можуть бути різні символи, наприклад символ двійної кавички. В результаті відправивши кудись таким чином сформований JSON сервер не зможе програмно його десеріалізувати і буде видавати помилку (а розробнику важко буде зрозуміти в чому саме проблема). Тому рекомендую завжди коли є необхідність працювати з JSON формувати його структуру з об’єктів методом серіалізації – це однозначно допоможе уникнути подібних проблем, які буває важко визначити.
А для того, щоб завжди не писати багато символьні інструкції, буває зручно створити статичний клас, у якому добавити методи розширення, наприклад ToJson і FromJson – це може полегшити і пришвидшити роботу.
Надіюсь вам сподобається працювати з даними, описаними в форматі JSON.