Як обрати JSON парсер

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Всім привіт. Я Сергій Моренець, розробник, викладач, спiкер і технічний письменник. Хочу поділитися своїм досвідом роботи з таким форматом даних як JSON. Я познайомився з JSON на початку 2010 року, після дуже довгого періоду використання XML/SOAP. Крім того, на своїх тренінгах ми дуже часто розповідаємо про цей формат, особливо щодо роботи з REST-сервісами або мікросервісами. Я думаю, що ця стаття буде корисна для всіх, хто хоче поглибити свої знання або обрати JSON парсер для нового проекту.

Якщо глобально перейматися таким питанням, а яке призначення у форматів даних, то можна виділити три сфери застосування:

1) Зберігання налаштувань (конфігурації) проекту.

2) Обмін даними у розподілених системах та їх обробка.

3) Зберігання даних користувача (наприклад, у файлах або базах даних).

Більшість форматів створюється для якоїсь однієї мети. Наприклад, у properties або YAML файлах зручно зберігати налаштування, а ProtoBuf або Thrift використовувати для пересилання даних. І в жодному разі не навпаки. JSON унікальний тим, що його використовують для всіх трьох випадків, хоча і не завжди оптимально. Чому так сталося?

JSON з’явився на початку 2000-х, коли Дуглас Крокфорд, американський інженер, один з авторів JavaScript, вирішив створити новий формат, заснований на мові програмування, який він створив. По суті, JSON документ — це той же JavaScript об’єкт, який дозволяє зберігати найпопулярніші типи даних у розробці:

  • Примітиви.
  • Рядки.
  • Списки.
  • Вкладені документи.

По суті, єдиною відмінністю була відсутність коментарів і необхідність використовувати лапки для назв властивостей. Якщо хтось із вас застав цей час, то добре пам’ятає, що це була епоха царювання XML, який уже тоді багато програмістів не любили за громіздкість, але яким усі користувалися через відсутність зручних альтернатив. Тому поява нового формату справила справжній фурор, оскільки він був компактніший і зберіг при тому human-readability. Його почали використовувати, де можна, і де не можна. Дуже складно оцінити популярність форматів даних, але якщо скористатися статистикою пошукових запитів Google за останні 5 років, то JSON з великим відривом тримає перше місце, тоді як XML все більше йде в небуття.

Незалежно від того, який формат ви використовуєте у вашому проекті, у вас має бути можливість завантажити дані із зовнішнього джерела і перетворити на якусь in-memory Java структуру, швидше за все в POJO. Цей процес і називається десеріалізацією (а зворотний серіалізацією). При цьому в технічній літературі можна зустріти багато схожих термінів чи синонімів, таких як parsing, marshalling, decoding. Десеріалізація — це окремий випадок парсингу, тому що компіляція вихідного коду, наприклад, — це теж, по суті, парсинг, але не десеріалізація. Історично так склалося, що ці назви стали взаємозамінними, і про одну й ту саму бібліотеку можуть сказати і JSON парсер, і JSON serializer, і JSON процесор. Я в цій статті використовуватиму термін JSON парсер.

Потрібно відзначити, що парсинг не є атомарною операцією, а складається із трьох основних етапів:

1) Завантаження даних (із зовнішнього джерела) в оперативну пам’ять.

2) Перетворення на деяку проміжну (часто деревоподібну) структуру для доступу до вмісту або потокової обробки вмісту (Streaming API).

3) Перетворення в POJO об’єкт (опціонально).

Деякі бібліотеки не можуть самостійно виконати третій етап, і до них потрібно підключити зовнішній mapper.

Чому взагалі постає питання про вибір парсера? Невже Java за 26 років його так і не додали? На жаль, ні. На відміну від XML парсера (JAXB), який був включений в Java EE і навіть Java SE 6 в 2006 році, в останніх версіях Java немає ніяких компонентів для роботи з JSON. Більше того, навіть JAXB пакет видалили в JDK 11, вирішивши, що немає сенсу захаращувати JDK парсерами, а краще віддати це на реалізацію стороннім вендорам. Правда, в Java EE 7 в 2013 році нарешті з’явилася специфікація Java API для JSON Processing (JSON-P), а в Java EE 8 в 2017 році додали Java API для JSON Binding (JSON-B), але до цього часу їхній поїзд давно пішов. До того ж, зараз проектів на Java EE досить мало. Але про все по порядку.

Отже, ви вирішили використати JSON, але не знаєте, який парсер краще використовувати. Розберемо всі три випадки.

  • Ви зберігаєте конфігурацію (налаштування). У такому разі ви маєте наступні початкові умови:
  • завантаження/збереження конфігурації відбувається не так часто;
  • ви оперуєте порівняно невеликими обсягами даних;
  • конфігурація зберігає внутрішні дані вашого проекту, структура яких рідко змінюється і її можна вважати умовно статичною, тому її зручніше мепіти на ваші POJO для подальшої обробки в Java коді, і вам важливо, щоб парсер був досить гнучким у плані налаштувань;
  • ви можете дозволити собі завантажити/зберегти конфігурацію цілком і не звертати увагу на ефективність цієї операції.

З такими умовами для вас головне — мати простий і зручний парсер, що включає API з мінімальним порогом входження, досить популярний, щоб він був знайомий більшості розробників, досить зрілий і стабільний, щоб ще довго підтримувався та розвивався. Зараз таких проектів два — Jackson та Gson.

Jackson — проект, який було розпочато у 2007 році у компанії FasterXML, і спочатку це був мінімально простий парсер, який підтримував лише JSON. Такий проект був життєво необхідний, тому що популярність JSON набирала обертів і потрібна була відповідна технологія для роботи з ним. У 2009 році вийшла версія 1.0 під кодовою назвою Hazelnut. Поступово Jackson почав підтримувати все більше форматів даних, починаючи від XML і закінчуючи новими бінарними форматами. Таким чином, основний модуль підтримував лише JSON, а решту можна підключити як додаткову залежність. Ще одна крута фішка Jackson — великі можливості для кастомізації парсингу завдяки анотаціям і вбудованим конвертерам. Ще одна його перевага — інтеграція з Spring Framework, JAX-RS та Guice.

Якщо вам потрібно серіалізувати Java об’єкт, код для цього складається всього з трьох рядків:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
String json = objectMapper.writeValueAsString(objectMapper);

Що таке ObjectMapper? Це потоково-безпечний (thread-safe) об’єкт, який є обгорткою для такого об’єкта як JsonFactory і реалізує дві головні операції:

  • Серіалізацію за допомогою JsonGenerator API
  • Десеріалізацію за допомогою JsonParser API

Таким чином, якщо ви створили свій файловий формат, ви можете написати власну JsonFactory та використовувати ObjectMapper разом з нею. Jackson — єдиний з JSON парсерів, який підтримує Java records (з’явилися в Java 14), таким чином, ви можете використовувати їх як DTO, наприклад

Для того, щоб змінити/налаштувати mapping, є великий набір Jackson анотацій, котрий з роками тільки розширюється:

@Getter
@Setter
@JsonNaming(UpperCamelCaseStrategy.class)
public class Book {
      
       @JsonProperty("title")
       private String name;
      
       @JsonIgnore
       private byte[] content;
 
}

Gson — альтернативний проект, який розпочав своє життя в ІТ у 2008 році. Спочатку це був внутрішній продукт Google, який був open-source, але при цьому так і не підтримувався і не просувався офіційно Google. Він підтримує тільки JSON формат і пропонує базові можливості для кастомізації, включаючи лише 5 анотацій (у Jackson 35(!)). Розробка продукту ведеться набагато більш розмірено, поточна версія 2.8.8, а версія 2.0 випущена ще в 2011 році. Його API мінімально простий:

Gson gson = new Gson();
String json = gson.toJson(user);

Який із двох проектів вибрати? У плані зручності API вони приблизно однакові, але Jackson перевершує конкурента за можливостями розширення, підтримки нових форматів та налаштувань. Крім того, він вбудований в Spring MVC і інтегрований в Spring Boot, тому якщо у вас Spring проект, то ви швидше за все виберете Jackson. Щоправда, є ще нефункціональні вимоги, і ми про них поговоримо трохи згодом..

  • Тепер уявімо, що ви використовуєте JSON формат для відправки повідомлень між вашими сервісами (зовнішніми або внутрішніми), тут також можлива додаткова обробка повідомлень.

Взагалі кажучи, JSON формат не найідеальніший вибір у цьому випадку через свою надмірність. Більше того, навіть бінарні формати, які, як Bson, MsgPack або CBor, забезпечують додатковий стиск не більше 20-30%. І я б порекомендував використовувати спеціально розроблені формати для такого сценарію, такі як ProtoBuf, FlatBuffers або Apache Thrift, які передбачають наявність схем та додаткової оптимізації. Але це тема для окремої статті, а ми повернемося до JSON. Отже, у цьому випадку нам потрібний максимально швидкий парсер, до того ж бажано з мінімальним споживанням пам’яті. Розглянемо дві цікаві бібліотеки — Json-iterator і Json Smart.

Json-iterator — найновіший парсер із усіх, що існують, який почав розроблятися у 2016 році. Дивно тут те, що було створено дві реалізації, одна з них на Go. Його головний плюс — висока продуктивність, підтверджена benchmarks. Окрім того, тут є унікальний динамічний режим роботи на основі бібліотеки Javassist.

Один з відносних мінусів будь-якого парсера — при десеріалізації він нічого не знає заздалегідь про наші POJO класи, тому змушений використовувати порівняно повільний Reflection API для доступу до полів/методів. Бібліотека Javassist дозволяє створювати байт-код на лету, і json-iterator з її допомогою генерує оптимізовані decoders/encoders для кожного використовуваного POJO класу:

JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH);
JsonStream.setMode(EncodingMode.DYNAMIC_MODE);

Це теоретично має прискорити десеріалізацію. Ще один цікавий момент — Json-iterator підтримує три види API для парсингу:

  • Bind API (конвертуємо JSON в POJO объект)
  • Iterator API (низькорівневий обхід JSON документа)
  • Any API

Якщо Bind API та Iterator API достатньо зрозумілі, то що за невідомий звір Any API? Уявимо собі, що ви хочете конвертувати JSON в наступний POJO:

public class User {
       private String login;
       private String pasword;
       private Address address;
}

Її можна виконати за допомогою того ж таки Bind API. Але що, якщо вам адреса з JSON не потрібна у вашому випадку? Тоді можна додати анотацію @JsonIgnore або видалити поле address з класу User. А що, якщо у вас складніший варіант, коли адреса іноді потрібна, іноді ні? Якщо він не потрібен, ви все одно витрачатимете час на його парсинг. І тут вам допоможе Any API, який дозволяє робити lazy loading для окремих елементів:

public class User {
       private String login;
       private String pasword;
       private Any address;
}

Тепер за промовчанням поле address ніяк не заповнюватиметься. Але ви завжди зможете його ініціалізувати/завантажити за запитом:

Any address = user.getAddress();
String country = address.get("country").toString();

Json Smart — ще один проект, який позиціонувався як найшвидший парсер. До його переваг можна віднести компактність (немає зовнішніх залежностей), робота на JDK 5+ і легку міграцію з ще одного легковажного парсера json-simple.

До цікавих особливостей цього проекту варто віднести те, що він підтримує два режими роботи — звичайний і silent (коли він ігнорує всі помилки та exceptions):

ExampleRecord exampleRecord = JSONValue.parse(userJson, ExampleRecord.class);
ExampleRecord exampleRecord = JSONValue.parseWithException(inputJson, ExampleRecord.class);

Який із двох парсерів вибрати? Для цього ми трохи пізніше порівняємо їхню продуктивність у тестах.

  • Останній варіант використання JSON, який буде зберігатися в файлах користувача або базі даних

У цьому випадку ми часто маємо справу з неструктурованим (raw) JSON, коли нам доводиться запитувати дані про поля, які ми дізнаємося в run-time. Тут нам не допоможе binding, і нам потрібно вибирати між конвертацією JSON у Map:

       objectMapper.readValue(inputJson, Map.class);

і використанням JsonNode API, коли ми працюємо з JSON як з деревом:

       JsonNode tree = objectMapper.readTree(inputJson);
       System.out.println(tree.get("glossary").get("title").textValue());

Такий спосіб простий і зрозумілий, але вимагає великої кількості коду і особливо незручний, якщо формат або тип запиту часто змінюється. Іноді зустрічається ще більш складне завдання — знайти поля/об’єкти в JSON лише за їхньою назвою. І тут нам знадобиться бібліотека Jayway JsonPath.

Json Path часто називають регулярними виразами для JSON, у цьому сенсі вона аналогічна бібліотеці XPath. За допомогою її DSL можна легко написати найскладніший запит для отримання інформації з JSON. Це її єдине завдання, тому вона не вміє робити конвертувати JSON в POJO, ні зберігати POJO в JSON. Щоправда, ви можете підключити Json Smart/Jackson для такого binding.

Отже, як нам отримати, наприклад місто користувача?

String city = JsonPath.parse(inputJson).read("$.address.city", String.class);

Тут $ — посилання на кореневий елемент JSON (аналогічно this в Java), а для навігації по вкладених документах можна використовувати крапку. Що якщо ми не знаємо, де точно знаходиться поле city? Не біда, для цього є спеціальний вираз — дві крапки:

String city = JsonPath.parse(inputJson).read("$..city", String.class);

Якщо такий синтаксис все ж таки здається вам незвичним, є спеціальний сайт, де ви можете в ньому попрактикуватися. Але у JsonPath є й складніші операції. Наприклад, вам потрібно отримати всіх користувачів з України, тоді для цього є такий вираз:

$.address[?(@.country == ’Ukraine’)]

Тут @ - це посилання на поточний JSON елемент, що обробляється. Через свою простоту JsonPath дуже зручно використовувати в тестах, коли нам потрібно перевірити наявність даних у JSON і нас не дуже непокоїть питання швидкодії.

Нефункціональні вимоги

Крім зручності та простоти використання бувають й інші характеристики бібліотек, у таблиці нижче я порівняв усі 5 проектів за 5 параметрами:

  • Розмір jar-файлу (ів)
  • Кількість комітів
  • Число розробників на проекті
  • Кількість питань з таким тегом на Stackoverflow
  • Кількість бібліотек, які використовують цю технологію (як залежність)

Ці показники допомагають оцінити, наскільки сильно бібліотека ускладнить ваш проект, наскільки вона активно підтримується та розвивається, оскільки це опосередковано впливає на випуск нових версій та виправлення помилок, на її популярність, розмір community та наскільки швидко ви зможете знайти відповідь в Інтернеті на ваше запитання або вирішити проблему.

І тепер найцікавіший показник — швидкодія. Скористаємося знайомою бібліотекою JMH (Java Microbenchmark Harness) та порівняємо у часі (в наносекундах) виконання 4 типових операцій:

  • Десеріалізація JSON в Java объект
  • Десеріалізація JSON в Map
  • Серіалізація Java объекта в JSON
  • Запит на отримання властивості за назвою

Для цього використовуємо наступну конфігурацію:

  • JMH 1.33
  • JDK 17.0
  • Intel Core i9, 8 cores
  • 32 GB
  • 10 ітерацій обчислення, 10 ітерацій прогріву (warm-up)

В результаті отримаємо наступні результати:

Тут цікаво кілька показників

  • Jsoniter показав найкращу швидкодію у більшості тестів
  • Jackson виявився найкращим під час серіалізації Java об’єкта (на другому місці Json Smart)
  • Json Path найшвидше десеріалізує JSON у Map

Якщо ви пам’ятаєте, то Json-iterator дозволяє генерувати декодери/кодери на льоту за допомогою бібліотеки Javassist. Якщо ми увімкнемо цей режим, то benchmarks покажуть час 440 (нс) для першої операції, таким чином ми отримали прискорення у 2.5 рази і так для найшвидшого парсеру.

Чи всі існуючі JSON парсери ми розглянули? Я почав цю статтю з того, що представив автора JSON формату Дугласа Крокфорда, але ніяк не згадавши його під час розповіді про існуючі JSON парсери для Java. Насправді Крокфорд 2010 року розробив свою канонічну бібліотеку, яку назвав JSON-Java. У 2014 році він передав справи іншому розробнику Шону Лірі і більше не бере участі у її розробці.

JSON-Java — дуже проста бібліотека, яка підтримує декілька базових операцій та не містить жодної анотації.:

  • Перетворення об’єкт -> JSON
       JSONObject obj = new JSONObject(user);
       String json = obj.toString();

  • Парсинг або створення JSON об’єкта вручну
       JSONObject obj2 = new JSONObject(json);
       obj2.append("name", "Jones");

Ця бібліотека відрізняється найменшим розміром — 69 кб, але я можу її порадити зараз для навчальних проектів або там, де є серйозні обмеження за розміром дистрибутива (Android).

Чи варто використовувати JSON-B та JSON-P для нових проектів? З одного боку, це стандарт, уніфікація для enterprise додатків, з іншого боку, він з’явився досить пізно, коли Java-індустрія звикла до таких бібліотек як Jackson/Gson. В іншому, якщо у вас Java EE/Jakarta EE додаток, це може бути правильним вибором, тим більше що ці проекти активно розвиваються і скоро готується версія 2.1. Ви можете вибрати між кількома реалізаціями цієї специфікації, такими як Apache Johnzon та Eclipse Yasson.

З тієї ж причини я не розглянув деякі інші відомі JSON парсери, наприклад Simple JSON, який вже майже 8 років як не оновлюється, LoganSquare, який покинутий з 2016 року і багато інших.

Висновки

У цій статті ми розглянули основні JSON парсери для Java додатків і основні сценарії роботи з JSON. Якщо підсумувати, то я міг би порадити таке:

  • Для парсингу JSON загального призначення використовувати бібліотеку Jackson як найбільш просунуту та гнучку в плані конфігурації. Іноді буває трохи інша вимога — потрібен простий невибагливий JSON парсер для тестів, тут я б порекомендував Gson знову ж таки через його простоту і невеликий розмір.
  • Якщо вам потрібна максимальна швидкодія, використовуйте JsonIterator (або JSON Smart)
  • Для роботи з неструктурованим JSON, складних запитів, особливо в інтеграційних тестах, використовуйте Json Path
👍НравитсяПонравилось7
В избранноеВ избранном4
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
В результаті отримаємо наступні результати

В яких одиницях?

та порівняємо у часі (в наносекундах) виконання 4 типових операцій:

А взагалi то дивно, що нема станданого сереалiзатора. Тому у нас Jackson, хоч воно й з ще того столiття.

Мені здається, його немає в Java Core, тому що розробники JDK вважали його «enterprise feature», і вони таки додали його в 2013 році в Java EE у вигляді специфікацій JSON-B/JSON-P. Щоправда, вони дуже запізнилися із цим.

Набіса пхати в JDK JSON парсер, якщо він далеко не всім є необхідний?
Навпаки, куди логічніше мати його як окрему бібліотеку/модуль.

Це дурнця яка вже з JAXB відбулась — напхали всякого в JDK, а тоді зрозуміли що дуже жирне воно виходить в результаті, при тому що далеко не всім і не завжди воно буде потрібно. Тому project Gigsaw тобто модулі Java 9.

Це не таке однозначне питання — потрібен чи не потрібен у JDK парсон JSON.
З одного боку, підтримку JSON додали в Java EE у 2013 році (як специфікації JSON-B та JSON-P). З іншого боку, що робити тим, хто не використовує Java EE?

Чому не однозначне? Однозначне.
Є купа софта який не працює з JSON. Для чого цьому софту «жирніша» JDK з функціоналом, який не буде використовуватись?

Для чого взагалі project jigsaw робили по вашому?

Є купа софта який не працює з JSON.

Импортируя почти любую библиотеку, хорошо, если ты используешь 30% ее апи.
Могу сказать как в .NET сделали. Есть стандартный сериализатор\десериализатор xml\json и куча сторонних, которые можно импортировать в проект.

Импортируя почти любую библиотеку, хорошо, если ты используешь 30% ее апи.

Саме так. Тим менше причин вбудовувати цю бібліотеку в runtime, який є спільним для абсолютно всіх.

вбудовувати цю бібліотеку в runtime

Не знаю как в джаве, но в дот нете, любой конфиг это уже json. Тоесть чтобы просто стартануть стандартное кор приложение с appsettings, нужно откудато этот парсер взять.

любой конфиг это уже json

А якщо хочеться законфігати в YAML — в дотнеті не варіант? Ну тобто варіант, але при цьому зайвий JSON парсер в рантаймі буде бовтатись?
Що сказати — щиро співчуваю.

Колись всі молились на XML і пхали парсери і маппери в рантайм — тепер випилюють.

Це саме цілком може повторитись з JSON-ом, який замінять на якийсь YAML для human readable конфігів, і який-небудь protobuff для серіалізації.

І будуть знову лікті кусати.

P.S. Пам’ятаю колись в Windows ще в 90-х були ini файли для human readbale конфігів — вже зручніше ніж JSON в блокноті правити було :D

А даты вам не нужно считать по календарю Майа ? Может тогда и стандартный System.DateTime выпилить с рантайм. Ну вдруг. И работа с файлами не нужна, может у вас хипстерское облако. Долой System.IO из рантайма.

YAML куди більш розумний вибір ніж редагувати конфіги в JSON.

До чого тут календар Майя? Він нафіг нікому не вперся. На відміну від YAML чи того ж таки INI який вже сто років в віндах скрізь юзаєтся.

P.P.S. Нагуглив таке — docs.microsoft.com/...​?view=dotnet-plat-ext-5.0

INI-based configuration provider. От і «любой конфиг уже json». Може вам для початку дотнет підівчити б?

YAML куди більш розумний вибір ніж редагувати конфіги в JSON.

Так напиши свой дотнет, где все конфиги будут читаться в YAML вместо xml и json

INI-based configuration provider. От і «любой конфиг уже json». Може вам для початку дотнет підівчити б?

А еще можно читать конфиги как плеин текст из текстового файла, ректально пытаться переопределить логику. Что это должно менять ?

Я вот ради интереса открыл только что папочку своего тестового (пустого) консольного приложения и внезапно там уже торчит файл proj в формате xml и три конфига в формате json.

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

Так напиши свой дотнет, где все конфиги будут читаться в YAML вместо xml и json

Не вказуй що мені робити, хлопче. Краще йди загугли YAML бібліотеку під .Net — вони існують.

Я вот ради интереса открыл только что папочку своего тестового (пустого) консольного приложения и внезапно там уже торчит файл proj в формате xml и три конфига в формате json.

Тобто вже два різні формати. Це успіх!

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

Через IniConfigurationProvider же ж — написав же!

Через IniConfigurationProvider же ж — написав же!

Тоесть ты настолько одаренный кодер, что в сраное консольное приложение, которое не выводит даже единой строчки на экран, уже готов запихнуть IniConfigurationProvider, с альтернативно одаренным форматом файлов из 90х для платформы виндоуз, только потому что тебе не нравится гетеросексуальный json ?

IniConfigurationProvider вже запханий в .Net, і зовсім не мною. те, що ти як «одарьонний кодер» не знав про його існування — лише вказує на твій рівень.

не знав про його існування — лише вказує на твій рівень.

Я про него знаю уже наверно 20 лет. Но речь не о мне. А о, хм, скажем так, человека с элементом профнепригодности, который не понимает почему INI не может заменить Json by design. Другими словами Json это текстовый снапшот любого иерархического обьекта (которым может быть сложная конфигурация, как частный случай). А вот INI файл это набор ключей, и как без белой горячки в него можно сериализовать обьекты с любой вложенностью элементов, любой вложенностью массивом я чесно хз.

человека с элементом профнепригодности

А це та людина, яка не знає як зробити, і визнає це

я чесно хз.

Загуглив би перше хоча б — в Гуглі забанили?
github.com/madmurphy/libconfini
«With libconfini you will find in INI files the same serialization power that you would normally find in other heavily structured formats (such as JSON, YAML, TOML), but with the advantage of using the most human-friendly configuration format ever invented (thanks to their informal status, INI files are indeed more fluid, expressive and human-friendly than formats explicitly designed with the same purpose, such as YAML and TOML).»

Окремо веселить оця дурня:

иерархического обьекта (которым может быть сложная конфигурация, как частный случай).

Не треба юзерів задовбувати своїми «ієрархіями об’єектів» серіалізованими в JSON, XML і тп. Конфігурація це КОНФІГУРАЦІЯ, а не серіалізована ієрархія об’єктів (уяви, є ще серіалізація для випадків де об’єкти мають зациклені вказівники, там взагалі не ієрархія/дерево/DAC, там цикли — може ще таке юзерів будеш змушувати в текстовому редакторі редагувати? Щоб повна «глибина глибин»).

Немає чого змушувати юзерів страждати редагуючи серіалізовані графи об’єктів. Вже YAML за це критикують що занадто все ускладнили. Але якщо вже кров з носа треба щоб конфіг був саме таким — є для цього YAML, який все одно куди більш людський ніж JSON, і принаймні підтримує коментарі!

Але ні — захаєм в рантайм JSON парсер, а хто хоче YAML то «хай напише свій дотнет» :D

Це просто фініш.

Конфігурація це КОНФІГУРАЦІЯ

Тоесть ты решил расписаться за все приложения, за сложность их конфигов, навязать им вместо универсального формата json, который читается на всех платформах вплоть до кофеварок, хорошо читается человеком, быстро парсится и содержит минимум оверхед информации для хранения и передачи, на какойто хендмейд формат INI в котором можно хранить только ключи и только на винде ? Или YAML. Ну еще раз. Это не форум ЛГБТ сообщества, да еще с поломанным мат аппаратом, который не понимает почему для одних задач хорош XML а для других JSON и почему INI это ошибка эволюции форматов данных, который выплюнули в 90х

Тоесть ты решил расписаться за все приложения, за сложность их конфигов, навязать им вместо универсального формата json, который читается на всех платформах вплоть до кофеварок, хорошо читается человеком, быстро парсится и содержит минимум оверхед информации для хранения и передачи, на какойто хендмейд формат INI в котором можно хранить только ключи и только на винде ?

Я тут до чого? INI парсер в рантайм дотнету включив не я :D

Але я бачу ти то точно знаєш що всі аплікухи мають мати конфіг в JSON і саме в JSON. Але ще в XML :D Ну щоб для вірності.

Але точно не в YAML. Хоча він якраз «читается на всех платформах вплоть до кофеварок, хорошо читается человеком, быстро парсится и содержит минимум оверхед информации» на відміну від нечитабельного JSONа та дуже verbose XMLя. І окремо має перевагу перед JSON у вигляді підтримки коментарів. Про яку ти встидливо мовчиш.

почему INI это ошибка эволюции форматов данных, который выплюнули в 90х

А YAML теж помилка 90х?
А XML?

Ти ж сам всім напарюєш JSON, хоча вже на ці граблі з XML наступив. Але нічому не навчився.

INI парсер в рантайм дотнету включив не я

Я в шоке. Он включен _для обратной совместимости_.
На винде 95го года уже были ини файлы (ну это на вскидку, может и 98го года, но формат очень древний). Создали его по принципу «вася запили чтото быстренько, нужно пару строчек настроек хранить на диске»

І окремо має перевагу перед JSON у вигляді підтримки коментарів. Про яку ти встидливо мовчиш.

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

Ивиняюсь, я был не прав

На винде 95го года уже были ини файлы (ну это на вскидку, может и 98го года, но формат очень древний).

В версии Windows 1.01 это был только файл WIN.INI. В Windows 3.0 добавился файл SYSTEM.INI. А затем их количество начало расти быстро и бесконтрольно.

с*ка он был уже в винде 80х годов. И автар топит чтобы выбросить джисон и вернуться к ини файлам.

Выучи наконец формат JSON

Ти перший. Почни хоча б з Вікіпедії, коли вже в Гуглі тебе забанили

в джисон конфигах вполне можно оставлять комментарии

В „джисон” (g-son?) може і можна, а в JSON — ні.

en.wikipedia.org/wiki/JSON

Читаєм уважно:
JSON does not provide syntax for comments.[25]

Comments were intentionally excluded from JSON. In 2012, Douglas Crockford described his design decision thus: "I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability."[25]

А заодне і це
JSON is intended as a data serialization format.

While JSON is a data serialization format, it has seen ad hoc usage as a configuration language. In this use case, support for comments and other features have been deemed useful, which has led to several nonstandard JSON supersets being created. Among them are HJSON,[45] HOCON, and JSON5 (which despite its name, isn’t the fifth version of JSON).[46][47] The primary objective of version 1.2 of YAML was to make the nonstandard format a strict JSON superset.[48]

Не знаю какой там формат спецификации поддерживается в джаве, но у MS все есть. Даже подсветка присудствует.
ibb.co/bQNrXk6

И это не единственный способ оставлять комментарии.
Можно и банально через служебный аттрибут «_comment».

Не знаю какой там формат спецификации поддерживается в джаве

:D

До чого тут Java взагалі, коли сам автор формату JSON чітко висловився про коментарі — і в JSON їх немає. Але ж ти краще знаєш за самого Дагласа Крокфорда, ну хто він такий а хто ти? :D

Якщо в MS є знову свій особливий парсер, який ігноруючи стандарт додає нестандартний екстеншн в формат — то це вже робить конфіги у MS не JSON-ом а своїм особливим форматом на кшталт HJSON. Сміщно читати як комент тому ти розпинався про стандарти і кросплатформенність, а тепер кажеш «а от в MS» :D

Ну во-первых слив засчитан. То что у тебя не поддерживалось, внезапно поддерживается. Комментарии можно оставлять как минимум 2 способами (даже 3мя) в мс студии, и как минимум одним способом в спецификации Дагласа.

Это первое. Второе, твоя далекость от разработки продолжает быть далекой. Потому что сплошь и рядом у нас есть 100500 спецификаций и каждый продукт поддерживает только свое видоизмененное подмножество спецификации. Это касается HTML, SQL, CSS и в том числе JSON.

Так что роль Дагласа или Дугласа не более чем распечатать бамажку, а что и как поддерживается, уже решают конкретные программные продукты. И в этом смысле я полностью на стороне МС, комментарии в json конфигах вполне не лишние.

Коментарі в конфігах очевидно не зайві, але в JSON коментарів немає — які б дурниці ти не писав. JSON+коментарі це HJSON чи щось інше, але не JSON.

А от YAML коментарі підтримує по стандарнту. Але до тебе це просто не доходить.

Ти вже пишеш повну дурню, що стандарти не стандарти — але це зовсім не так, і приклади типу HTML лише показують які проблеми і комтилі виникають, коли стандарти не достатньо чітко прописані (один лише xhtml згадати). При тому що ти щойно сам топив за стандарти бо кросплатформенність, інтероперабельність і оце все — а тепер виявляється най кожен інтерпретує стандарти як заманеться, «это норма». Це повний LOL.

Тому злив зарахований саме твій.

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

Ну возьми добавь свои комментарии так, что пролезут через любой парсер

{"_comment": "Комментарий для нашего танцора"}

Даглас тебя както ограничил ?

А от мене не дивує вже зовсім коли замість рішення косорукі нуби пхають скрізь костилі. Нафіга так корячитись, аби тільки YAML не юзати? Що за мазохізм?

Нафіга так корячитись, аби тільки YAML не юзати?

Проблема в том, что этот ЯМЛ заходит только вайтишникам. Которые в своей жизни парсеры разве что для ини файлов писали. Вообще любой писатель языков знает, что изящество и мощь синтаксиса, измеряется ограниченным лаконичным набором операторов, его рекурсивной красотой и единообразием. Вот Джисон нормальный язык, у него есть принцип самоподобия. У него всего два вида скобок, ковычки и двоеточие описывает иерархии любой сложности. Глаз видит это фрактальное единообразие и сразу понимает что происходит. За секунды выучивает синтаксис, как будто на нем всю жизнь писал. В нем есть это изящество.

А ЯМЛ его разрабатывали калеки, как синтаксис с набором исключений. Вот здесь мы отступами нарезаем, а здесь у нас коммент, а здесь куйнем все в одну строку, здесь имеют значения переносы строк. Здесь у нас были ковычки, а здесь их уже нет. Не язык а набор костылей из костылей под каждый вид исключения. Потом на этот каличный язык пишут такой же парсер, который состоит из наборов сраных ифов, флажком и прочей куиты, чтобы обработать каждое исключение в этом синтаксисе. Которая конечно же сказывается на быстродействие. И этот самый ЯМЛ таким же и заходит. Все остальные 95% сидят на джисон, а в сложных кейсах на хмл.

Цей комент суцільний лол. А «кОвички» і «джи-сон» — вишенка на тортику

Я и не сомневался что до тебя мой коммент не дойдет. Вайти сейчас натягивает всяких кадров с бекграундом асфальтоукладчиков. Потому у нас и ямочный ремонт херовый и айти так себе.

Ти явно з них, костильобудувач.

Он включен _для обратной совместимости_

Ще останнє питання — хоча бачу тут як об стіну горохом.
Скільки ще парсерів треба включити в рантайм для зворотньої сумісності? INI є, XML є, JSON є — скільки ще? Набіса це все тягнути САМЕ В РАНТАЙМ?

автар топит чтобы выбросить джисон и вернуться к ини файлам

От ти смішний — я топлю за те, щоб ВИКИНУТИ ВЗАГАЛІ ВСІ ПАРСЕРИ З РАНТАЙМУ! А вже включати кожен в свій софт буде те і тільки те, що саме йому потрібно (якщо взагалі щось).

Ти навіть суті посилу оригінального не зрозумів.

Довбешся тут з JSON vs INI бо не знаєш більше чого вчепитись. YAML тепер встидливо ігноруєш :D

Скільки ще парсерів треба включити в рантайм для зворотньої сумісності? INI є, XML є, JSON є — скільки ще? Набіса це все тягнути САМЕ В РАНТАЙМ?

Тянут только то, чем нужно парсить конфиги. Было бы странно чтобы рантайм не смог распарсить свои конфиги перед стартом приложения. Не ?

YAML тепер встидливо ігноруєш :D

Я слышал Yet Another Markup Language Balabanovo Moskovskoy Oblasti в 100 раз круче чем джисон, YAML и INI.
Ти его встидливо ігноруєш !

А чому це я ігнорую? Я якраз за те, щоб в рантаймі не було «стандартного» парсера — і ти міг юзати який хочеш як лібу, а не бути прив’язаним до чогось що визначили за тебе н років тому.

Якщо ти такий ванат оцього балабаново то якраз я даю тобі свободу це л-но юзати — їж не обляпайся.

А от MS обмежує тебе до JSON. Ну або XML. Або INI. Але точно не YAML :D

А от MS обмежує

Добавлении парселки нового формата, или добавлении какойто кастномной реализации парсера (например тот же ньютонсофт либа для джисон) в студии идет в два клика мышки.

Почему меня не обмежует а тебя все обмежуют и обмежуют ?
www.nuget.org/packages/YamlDotNet

Горох об стіну.

Візьму я собі лібу з парсером для свого формату, не проблема — але мені довіском в рантаймі йде зе парсер іні, хмля і джейсона. Набіса мені то все? Аж тут понабігають такі як ти і наярюють що «юзай ті формати бо їх парсер вже є в рантаймі». От такі як ти мене і обмежують, дойшло до тебе нарешті?

только потому что тебе не нравится гетеросексуальный json

15 років тому такі ж умніки те саме один в один казали про XML. «Любой конфиг уже XML» і тд. Слово в слово казали — і де вони тепер?

Так ConsoleApp1.proj на моем компьютере в формате XML, если что. XML парсер из рантайма никуда не исчез. Тебе нужно подучить разницу между XML и Json, между ними таки есть разница. Это не два взаимозаменяемых формата.

Тобто вони були праві і будь-який конфіг це вже XML? А JSON тоді в тебе для чого, якщо вже є XML і парсер вбудований в рантайм? Для чого, га? :D

Те, що «ConsoleApp1.proj на моем компьютере в формате XML, если что. XML парсер из рантайма никуда не исчез» лише демонструє імбецилію рішення включити парсер в рантайм, через що тепер половина робить щось в XML, бо «вже є парсер в рантаймі», а друга половина робить в JSON, бо «XML застарів, і тепер JSON має його замінити а XML з часом зникне». А в результаті маєм роздутий рантайм з двома парсерами, один з яких по суті legacy.

Але коли ти скомпілиш свій проект і зашипаєш юзерам/клієнтам, то твоя софтіна хіба буде юзати XML? Для тих же конфігів хоча б. Ти ж сам щойно писав що конфіги вже скрізь в JSON.

Это не два взаимозаменяемых формата

Ага, а ще SOAP ніяк не заміняється RESTом, REST не заміняється gRPC і тд, ну просто немає прикладів цього :D
Якщо ти не осилив замінити XML JSON-ом — це ще не означає що це неможливо.

Перечитай мою фразу еще раз.

Тебе нужно подучить разницу между XML и Json, между ними таки есть разница. Это не два взаимозаменяемых формата.

Кстате отличный вопрос на собеседование. Можно ли написать универсальный конвертор файлов из XML в Json, а из Json в INI и прочий хендмейд.
Какие плюсы какие минусы каждого формата. В каком случае нужно использовать каждый из них.

Питання для особливо обдарованих на засипку. Я написав калькулятор під Windows. Набіса йому конфіг в JSON?

От як той же калькулятор, який йде з коробки в Windows.

Навіть якщо в нього буде якийсь конфіг раптом (нехай там стан типу scientific view vs simple), він змінюється лише через UI і зберігається в registry. То для чого мені взагалі JSON parser?

Може все ж таки логічно його випиляти з рантайму, і додавати як dependency тільки в той конкретний софт, який його потребує?

Ну проста ж як двері думка — якщо софтіна А потребує функціоналу Х то нехай вона імпортує лібу для того функціоналу Х, а решта софтін які не потребують цього функціоналу нехай і не тягнуть із собою рантайм, в який він буде дарма вбудований. Бери тільки те, що потрібно.

Ну як така проста і очевидна річ до когось може не доходити?

У тому й річ, що після появи модуляризації (проект Jigsaw) кожен компонент — це окремий пакет.
Якщо JSON парсер був би в JDK, це також був би окремий пакет. А в JDK є утиліта jlink, яка дозволяє видаляти (або залишати) непотрібні пакети, що не використовуються (в основному для Docker контейнерів).
Вам не потрібний вбудований JSON парсер — просто видалили б його та все. На роботу JDK це ніяк не вплинуло б.

Саме так. Але до project Jigsaw цього зробити не можна було — тягнеш весь JDK з усім зайвим барахлом.

Вам не потрібний вбудований JSON парсер — просто видалили б його та все

От якщо у вас Java8 compatibility — то все, «не судьба». Мусите тягнути все барахло що включене в JRE8. Без варіантів.

І добре що в Java до 9-ї версії вже зрозуміли що це проблема, і зробили project Jigsaw (та й «зрозуміли» то тільки тому що рантайм вже розжирів непристойно). Хоча по хорошому просто треба було одразу все крім самих базових речей робити окремими бібліотеками, а не пхати в стандартну бібліотеку / рантайм — тоді project Jigsaw навіть і не знадобився б. По факту Jigsaw це костиль для backward compatibility.

А в інших мовах програмування та рантаймах — де немаж ніяких project Jigsaw і все барахло тягнеш з собою, як оце в .Net (де тобі і XML парсер, і JSON, і навіть INI файл парсер з коробки запханий для зворотньої сумісності) — цього досі не второпали! Гірше того, он людина вище пише про «стандартний серіалізатор», мовляв дивно що немає :facepalm:

Не чув мабуть що в Java таки була стандартна серіалізація — яка давно deprecated. Хоча вона і могла більше ніж JSON серіалізатори — наприклад зациклені графи об’єктів зберігати а не тільки DAC/дерева, як більшість JSON серіалізаторв — але мала силу силенну інших недоліків.

Суть в тому що неможливо один раз зробити якийсь один парсер/серіалізатор «на всі часи». Навіть один формат обрати не можна — бо був колись пропрієтарний формат Java, потім XML, потім JSON, завтра буде YAML чи ще якийсь protobuf, і ще казна що. І немає «one size fits all» — комусь кров з носа потрібна оптимальність protobuf, а комусь навпаки куди важливіша human readability JSON-а, чи просто інтероперабельність з якимось рішенням яке вже існує і використовує XML, тп. А комусь може просто не підходить швидкодія «стандартного» рішення, або навпаки не швидкодія а затрати по пам’яті — і пишеться своє, більш оптимальне під конкретні вимоги конкретного випадку.

Тому не треба робити «стандартний» парсер/серіалізатор, і взагалі по можливості не треба робити нічого стандартного (HTTP клієнт, наприклад, чи ще щось таке) окрім самого-самого базового функціоналу. Все решта треба робити модулями. Змушувати всіх використовувати якусь одну «стандартну» реалізацію це шлях в нікуди, і дитяча хвороба нових технологій.

Якого того століття? В статті сказано

Jackson — проект, який було розпочато у 2007 році

Але є одне «але», з якого варто починати: JSON — це JavaScript Object Notation, звідти його і простота, і звідти ж його обмеження. Він цяця, якщо з одного боку комунікаційного протоколу є нативний движок JavaScript. В іншому разі — дивіться на більш нативні для середовища протоколи.

XPath мертвий. Як і його аналоги. Чому? Надзвичайна складність читання коду, і звідти ж — важкість його стабілізації. Протестувати майже нереально. Тобто, мертвонародженою є сама ідея — код виходить короткий, але в англійскій мові є досить вдалий термін — go haywire. Це те, що відбувається із вашою пам′яттю, коли ви в нього намагаєтесь вникнути.

Тому найкращий варіант — це код, який максимально легко читається. НЕЗАЛЕЖНО від того, на скільки десятих відсотка він вийде довшим. Якщо можна щось написати кодом, БЕЗ НЕОБХІДНОСТІ додавати умову вивчення ЧИТАЧЕМ якогось додаткового фреймворка чи навіть бібліотечки — так і треба зробити. Потреба в додаткових знаннях поза кодом — найвища ступінь збитковості для проекту, який планується розвивати в подальшому. І окрім грошей воно просто неймовірно жере час. Інакше кажучи, коли у вас тисячі місць застосування — так, варто покладатися на особливості фреймворків. Якщо одиниці — краще додайте власного коду аніж користуватися вже готовим, який потребує щось вивчити щоб лише прочитати.

Бенчмарк показує аж нічого цікавого. Парсинг та форматування JSON та йому подібних протоколів — не найвище навантаження. Якщо мова йде про WEB, то у вас майже усюди йде стискання на рівні HTTP, а це мінімум на 2 порядки тяжча операція. Іншими словами, вплив того, що ви показали в бенчмарку, не перевищує навіть 1%, і це якщо ви виключно ганяєте готові дані. В реальних проектах там і 0.1% не знайдеться, тобто перевага в часі не є суттєвою навіть по міркам HighLoad.

Додам ще ложку дьогтю у ваш бенчмарк — а ви витрати часу GarbageCollector після нього заміряли? Чи слона-то ми й не примітили? А витрату пам′яті? Бо так можна і до використанння Electron дійти — ніби все кошерно та по стандартам, а насправді — монстр франкенштейна.

А витрату пам′яті?

Там все плохо. Я вот недавно десериализовал в памяти 1000 джейсон-строк. У меня asp.net core процесс потолстел на 2 гигабайта.

гагага лол
Заскринить и показывать любителям днонета

Скорее, любителям жёлтых заголовков, потому что на иное этот «опыт» не тянет.

О, боги! Як це Вам вдалося? Що там за строки вже такi?

Дякую за розгорнутий коментар. Витрати пам’яті та роботу GC я не перевіряв, так як цієї функціональності немає в JMH, та й взагалі рідко зустрічаєш такі тести, зазвичай перевіряють лише швидкодію.
З приводу того, що використання HTTP споживає більше ресурсів, ніж серіалізація/серіалізація — у принципі згоден, хоча залежить від конкретного use-case.
Тому перекидати такі JSON об’єкти краще через messaging systems (ту ж Apache Kafka). Ці системи асинхронні, підтримують durability і batching, там можна досягти пропускної здатності в десятки мегабайт на секунду.

при десеріалізації він нічого знає заздалегідь

частка «не» пропущена?

В бенчмарку було б не погано бачити одиниці вимірювання + що мірялось, посилання на тест або якась коротка інфа. Парсери по різному перформлять на малих і великих обєктах.

Дякую за зауваженння. Я хотів це викласти, але стаття і так вийшло дуже об’ємною, тому скоротив цей блок, можливо окремо напишу про performance JSON парсерів.

Мне как-то попалось в проекте, что нужно сериализировать и десериализировать обьект частично.
Другими словами есть джисон

{
   "FirstName": "John",
   "LastName": "Cena",
   "Age": 18,
   "Notes": "bla bla bla"
}

И мне нужно задать что серизируем только FirstName и LastName.
И аналогично в обратную сторону. У меня есть полный джисон, а мне нужно в обьекте обновить только FirstName и LastName, не затрагивая остальные поля.

И я написал парсер который сериализирует чтото типа.

var json = SerializePart(obj, "{'FirstName':$, 'LastName':$}");

DeserializePart(obj, json, "{'FirstName':$, 'LastName':$}");

Интересно, есть такое в стандартных джава сериализаторах ?

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

Так. В джексоні це можна зробити як мінімум 3-ма різними способами різними (views, custom ser/der, annotation — JsonIgnoreProperties).

Все залежить від того, чи можете змінити код класів (POJO). Якщо так, то це завдання вирішується додаванням анотацій на поля або саме оголошення класу.
Якщо не можете, просто пишете свій кодер або декодер, для такого простого JSON це нескладно.

Кстате я вспомнил почему я писал свой сериализатор/десериализатор вручную (очередной велосипед).
1. Шаблон типа {’FirstName’:$, ’LastName’:$} задает внешний код, который я не контролирую. Шаблон может быть любым и обьект отправленный на сериализацию/десериализацию может быть любым.
2. Этот шаблон это не простой темплейт, в нем могут содержаться инструкции до какой глубины в сериализации\десериализации спускаться и сколько элементов читать в массиве внутри иерархий. Это все нужно, чтобы патчить обьекты выборочно, а не скопом типа возьмем и отошлем джисон в 100 мегабайт на клиент. Хотя в этом обьекте поменялось всего 20 полей.

С такими условиями эта задача играет новыми красками.

Думаю, варто додати в заголовок і теги що стаття стосується Java.
А стаття виглядає гарно.

Дякую, я бачу, що Java вказано як тег

Писав коментар, коли тега ще не було :)

Подписаться на комментарии