1. Ментальна модель JSON і XML
Якщо чесно, розробник рідко прокидається зранку з думкою: «А чи не порівняти мені сьогодні JSON і XML, поки кава не охолола?». Зазвичай формат обирають тому, що хтось інший уже зробив половину рішення за вас: зовнішній сервіс віддає XML, старе корпоративне API вимагає SOAP, бухгалтерія надсилає «чудовий» XML-файл, а ваш проєкт, навпаки, хоче простий звіт у JSON. І ось ви стоїте між двома світами: компактним «дані як структура» (JSON) і більш «документним» світом тегів (XML).
Важливо розуміти: JSON і XML — це не «краще/гірше», а різні моделі подання даних. Якщо обрати формат без критеріїв, ви легко потрапите в ситуацію, коли дані начебто передаються, але кожен наступний крок розробки починає нагадувати квест: «вгадай, де ми загубили типи / порядок / вкладеність / екранування».
Щоб обирати формат усвідомлено, корисно спершу домовитися, як ми взагалі подумки «бачимо» дані в кожному форматі. Це схоже на вибір контейнера для їжі: суп у пакеті технічно можливий, але ви не зобовʼязані так страждати. JSON і XML по-різному відповідають на запитання «що таке структура даних». І це напряму впливає на те, наскільки легко нам буде читати, валідовувати й генерувати ці дані в Kotlin.
JSON як дерево значень
JSON — це дерево, у якому вузли бувають чотирьох основних типів: обʼєкт (пари ключ—значення), масив (упорядкований список), примітив (рядок/число/булеве значення) і null. У попередніх лекціях ми буквально працювали з цим як із деревом JsonElement, і це відчуття дуже точне: кожен вузол — це значення, у якого є тип, і цей тип можна перевірити.
Практичний ефект: якщо ви бачите true або 123 у JSON, ви вже розумієте, що це булеве значення й число (хоча іноді й доводиться перетворювати з рядка, якщо дані «брудні»).
XML як дерево елементів документа
XML теж є деревом, але його вузли — це елементи (теги) з іменами, атрибутами й текстовим умістом. І ось тут починається філософія: XML історично виріс як формат документів, де важлива розмітка, вкладеність і сенс тегів, а не «тип значення» як такий.
Практичний ефект: майже все в XML за своєю природою схоже на рядки (текст в елементі або значення атрибута). Типізацію ви додаєте «домовленістю»: наприклад, <age>21</age> — це число лише тому, що ви так вирішили й ваш код так розпарсив.
3. Дані vs документ
У реальних проєктах найкорисніше запитання звучить так: ви передаєте дані (структуру для програми) чи документ (текст із розміткою, де важливі елементи, атрибути, порядок, іноді — змішаний уміст)? Це не «чиста теорія», а швидкий спосіб перестати сперечатися в стилі «JSON модний, XML старий» і почати обирати формат під задачу.
JSON зазвичай виграє, коли ви описуєте «ось обʼєкт, у нього поля, у полів тип, іноді масив». Це типовий формат для API, конфігів, звітів, обміну даними між сервісами.
XML часто виграє, коли ви описуєте «ось документ, у нього є структура, елементи, метадані в атрибутах. Іноді важливий порядок, іноді важливі простори імен». Це типовий формат для документообігу, деяких інтеграцій, старих стандартів і взагалі світу, де «теги» важливіші за «типи».
4. Порівняння за критеріями
Нижче — практична таблиця критеріїв. Це не «істина в останній інстанції», але хороший чек-лист, який справді допомагає ухвалити рішення й потім не шкодувати.
| Критерій | JSON | XML | Що це означає для Kotlin-коду |
|---|---|---|---|
| Читабельність для «даних» | Зазвичай простіше й коротше | Зазвичай багатослівніше | JSON швидше читати очима й простіше генерувати |
| «Типи» примітивів | Типи видно (true, 123, "text", null) | Усе схоже на текст | У JSON менше «вгадування типів», у XML парсинг майже завжди — руками або за правилами |
| Масиви/списки | Природний [...] | Робиться повторюваними тегами | У JSON простіше виразити «список однотипних речей» |
| Атрибути як окрема сутність | Немає (усе — поля) | Є атрибути | XML зручно зберігає «метадані» поруч з елементом |
| Порядок | У масиві важливий, в обʼєкті зазвичай ні | Порядок елементів часто сприймають як важливий | Якщо порядок значущий як частина сенсу — XML може бути природнішим |
| Коментарі | У строгому JSON їх немає | В XML є коментарі | Для «людських» конфігів XML іноді зручніший (але це палиця з двома кінцями) |
| Схеми/валідація | Існують підходи, але часто — «за домовленістю» | Історично сильна екосистема схем | Якщо у вас жорсткі стандарти й контракти, XML-світ часто більш «регламентний» |
| Розмір/шум | Зазвичай менший | Зазвичай більший | Для логів/мережевих відповідей JSON вигідніший за обсягом |
| Інтеграції | Дуже поширений у сучасних API | Дуже поширений у старих/корпоративних стандартах | Вибір часто диктується зовнішнім середовищем |
Невелике спостереження з життя: Kotlin-інструменти й екосистема люблять JSON, тому що це зручний формат для машинних звітів. Наприклад, документація з міграції на K2 згадує збереження build reports у JSON-форматі. Тобто JSON справді використовується як «машиночитний звіт».
5. Один і той самий сенс у JSON і XML
Порівнювати формати без прикладів — це як пояснювати плавання за книжкою: технічно можливо, але краще не треба. Давайте візьмемо один і той самий сенс і запишемо його двома способами. Нехай у нас є користувач і його теги.
Приклад даних
- користувач: id=1, name="Alice"
- теги: ["kotlin", "json"]
JSON-варіант
fun main() {
val json = """{"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}"""
println(json) // {"user":{"id":1,"name":"Alice"},"tags":["kotlin","json"]}
}
Тут масив — це масив, числа — це числа, рядки — це рядки. Жодних сюрпризів: структура дуже близька до структур даних Kotlin (Map/List/примітиви).
XML-варіант
fun main() {
val xml = """
<root>
<user>
<id>1</id>
<name>Alice</name>
</user>
<tags>
<tag>kotlin</tag>
<tag>json</tag>
</tags>
</root>
""".trimIndent()
println(xml)
}
XML виглядає більш «документно»: багато тегів, явна ієрархія, повторювані елементи для списку. І зверніть увагу: число 1 тут — текст "<id>1</id>". Те, що це Int, дізнається лише ваш парсер (або ваша валідація).
6. Типізація й атрибути
Типізація і чому в XML усе раптово рядок
Якщо ви колись ловили помилку «чому вік 21 порівнюється як рядок і виходить, що 9 “більше”, ніж 21», то ви вже інтуїтивно розумієте, чому типізація важлива. JSON допомагає тим, що примітиви типізовані на рівні формату. XML же залишає типізацію на совісті домовленостей і коду, який читає документ.
Практичний ефект №1: валідація потрібна в будь-якому форматі
І JSON, і XML можуть бути «синтаксично коректними», але «логічно сміттєвими». Ми вже проговорили це на JSON: parseToJsonElement() відповідає лише на запитання «це валідний JSON», але не на запитання «нам підходять значення». З XML аналогічно: навіть якщо XML добре сформований, він може містити порожні поля, неправильні діапазони, неочікувані елементи.
Практичний ефект №2: в XML ви частіше пишете перетворення
В XML ви майже завжди робите String -> Int? / String -> Boolean? самі. У JSON це теж буває (особливо якщо дані «брудні»), але за замовчуванням типи хоча б ближчі до очікуваних.
Атрибути XML: сила і джерело вічних суперечок
Атрибути — одна з причин, чому XML досі живе й не планує йти «на пенсію до музею давніх технологій». Атрибути дають змогу зберігати метадані прямо на елементі. Це зручно, але також створює «релігійні війни»: що робити атрибутом, а що — елементом?
Порівняймо два варіанти одного сенсу:
fun main() {
val xmlWithAttribute = """<user id="1"><name>Alice</name></user>"""
val xmlWithElement = """<user><id>1</id><name>Alice</name></user>"""
println(xmlWithAttribute) // <user id="1"><name>Alice</name></user>
println(xmlWithElement) // <user><id>1</id><name>Alice</name></user>
}
У JSON такого вибору майже немає: ви б просто зробили "id": 1. І в цьому є плюс: менше варіантів — менше плутанини в даних і коді.
7. Де JSON і XML доречні в Kotlin-проєктах
Де JSON особливо хороший у нашому курсі
Ми вже будували звіти, агрегували дані, робили пайплайни «взяли колекцію → порахували → сформували результат». І для таких задач JSON — майже ідеальний «контейнер для результату»: його зручно зібрати вручну, зручно зберегти, зручно надіслати як структуру.
До речі, в Kotlin-екосистемі серіалізація — це не «екзотика», а стабільний інструмент: таблиця стабільності компонентів Kotlin окремо згадує kotlinx-serialization як стабільну бібліотеку.
Мініприклад: JSON-звіт для нашого трекера витрат
Уявімо, що в нашому практичному застосунку (умовно «Expense Tracker») ми рахуємо суму витрат і хочемо зробити звіт.
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
data class Expense(val title: String, val amount: Int)
fun buildReport(expenses: List<Expense>): JsonObject {
val total = expenses.sumOf { it.amount }
return buildJsonObject {
put("total", JsonPrimitive(total))
put("count", JsonPrimitive(expenses.size))
}
}
fun main() {
val expenses = listOf(
Expense("Coffee", 5),
Expense("Pizza", 12),
)
val report = buildReport(expenses)
println(report) // {"total":17,"count":2}
}
Зауважте, як органічно це лягає на Kotlin-колекції: ми порахували числа й поклали їх у JSON як числа, не втрачаючи типи «дорогою».
Де XML може бути доречнішим
Іноді вибір «XML» — не тому, що вам захотілося пригод, а тому, що ви підʼєднуєтеся до світу, де XML уже стандарт. Це можуть бути документи, інтеграційні формати, галузеві стандарти, а іноді просто «історично склалося».
У Kotlin/JVM світі XML часто йде поруч із Java-бібліотеками та стандартними API. Навіть у змінах Kotlin можна зустріти згадки інфраструктури, повʼязаної з org.w3c (це сімейство інтерфейсів, які історично асоціюються з DOM-моделлю XML).
Але ключовий момент такий: у XML частіше потрібно заздалегідь домовитися про «правила подання». Наприклад, як ми кодуємо список: повторюваними тегами? окремим контейнером <items>? як називаємо елементи? Це не погано — просто це інший стиль.
8. Валідація і генерація: як не зламати проєкт
Формат не дорівнює бізнес-правилам
Зараз важливий момент, який часто ламає проєкти: люди думають, що якщо формат обрано «правильно», то валідація більше не потрібна. На жаль, формат — це лише синтаксис і базова структура.
Ми вже робили шар Ok/Error для JSON-валідації. Ось хороша звичка: незалежно від того, JSON у вас чи XML, тримайте в голові одну й ту саму архітектуру:
flowchart TD
A["Текст (JSON або XML)"] --> B["Парсинг: 'це взагалі коректний формат?'"]
B --> C["Валідація: 'дані підходять правилам застосунку?'"]
C --> D["Доменна модель / розрахунки / звіти"]
Ця схема рятує від типового болю: «у нас іноді падає в продакшені», тому що хтось надіслав коректний файл, але з порожнім id або відʼємною сумою.
Швидкий алгоритм вибору формату
Іноді хочеться простого правила, щоб не сперечатися нескінченно. Нехай це буде не правило, а короткий алгоритм ухвалення рішення, який у більшості навчальних і практичних задач працює дуже пристойно.
Спочатку ви відповідаєте на запитання: структура даних стабільна чи ні? Якщо структура нестабільна, для JSON у вас є чудовий інструмент — дерево JsonElement і безпечне вилучення.
Далі ви запитуєте: вам важливіші «дані як обʼєкт/масив» чи «документ як розмітка»? Якщо важливіші дані, частіше обирають JSON. Якщо важливіша документність, стандарти й сумісність із наявними XML-контрактами — XML.
І нарешті — найреальніший критерій: хто ваш «сусід по інтеграції»? Якщо зовнішній світ віддає XML, ви не переможете реальність мотиваційною промовою про JSON — ви читатимете XML. Якщо зовнішній світ говорить JSON — ви працюватимете з JSON.
Мінінюанс: не склеюйте рядки
Це маленька порада із серії «на граблі наступають усі, але можна хоча б у мʼяких капцях». Якщо ви генеруєте JSON, намагайтеся збирати його як структуру (buildJsonObject, buildJsonArray), а не склеювати рядками.
У Kotlin-документації про ідіоми навіть на прикладі Gson видно, що JsonElement використовується як «універсальний носій JSON-структури», який потім можна перетворювати на потрібний тип. Це та сама ідея, що й у нас із kotlinx.serialization.json: спочатку структура, потім (за потреби) рядок.
З XML схожа історія: якщо ви не використовуєте бібліотеку, то «склеювання рядків» стає майже неминучим. Тоді особливо важливі екранування й акуратність. І так, це саме той момент, коли програма починає мститися за «та й так зійде».
9. Типові помилки під час вибору й використання JSON/XML
Помилка №1: думати, що «якщо розпарсилося — значить коректно».
І JSON, і XML можуть бути синтаксично правильними й водночас містити беззмістовні для вашої програми значення. Наприклад, id = -10, порожнє імʼя, сума витрати 0 там, де вона не повинна бути нульовою. Тому шар валідації — не «додаткова опція», а обовʼязкова частина дизайну: парсинг відповідає за форму, валідація — за сенс.
Помилка №2: плутати «немає поля» і «поле є, але воно порожнє/нульове».
У JSON ми вже бачили різницю між «поля немає» і «значення null». У XML аналогічна проблема проявляється інакше: елемента може не бути, елемент може бути порожнім (<name></name>), елемент може бути самозакривним (<name/>), і це часто означає різні речі. Якщо не домовитися про семантику, ви отримаєте купу дивних if.
Помилка №3: очікувати, що в XML типи прийдуть «самі».
У JSON типи примітивів видно одразу, а в XML майже все виглядає як рядок. Початківці часто пишуть код так, ніби <age>21</age> — це «вже число», і порівнюють як рядок, або забувають обробити нечислове значення. У XML потрібно заздалегідь планувати безпечні перетворення та повідомлення про помилки, інакше дані «зламають» ваш сценарій у найбільш невідповідний момент.
Помилка №4: безконтрольне змішування атрибутів і елементів в XML.
Коли одні розробники кладуть id в атрибут (<user id="1">), а інші — в елемент (<id>1</id>), у вас виходить два «підформати» всередині одного формату. Потім хтось пише парсер під один варіант, а дані приходять в іншому. Результат — помилка, яка виглядає як «іноді не працює», тобто найбільш дратівливий тип проблеми.
Помилка №5: будувати JSON/XML «конкатенацією рядків», а потім дивуватися, що все ламається.
Склеювання рядків майже гарантує помилки: забули кому в JSON, не екранували & або < у XML, випадково додали зайве перенесення рядка — і все: парсер незадоволений, користувач незадоволений, ви незадоволені життям. Якщо є можливість збирати JSON як дерево — робіть так. Для XML краще теж прагнути до структурного побудування, але в межах цієї лекції ми хоча б фіксуємо проблему: рядкове збирання крихке.
Помилка №6: обирати формат «бо він подобається», а не тому, що він підходить задачі.
Любити JSON — нормально. Любити XML — теж нормально (головне, щоб це не стало вашою єдиною рисою характеру). Але формат обирають за критеріями: що диктує інтеграція, що простіше валідовувати, де важливий порядок, де потрібна компактність, хто читатиме дані — людина чи програма. Якщо критерії не проговорені, рішення зазвичай виходить випадковим, а ціна випадковості — час команди й якість продукту.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ