JavaRush /Курсы /Kotlin SELF /JSON vs XML — практические критерии выбора формата

JSON vs XML — практические критерии выбора формата

Kotlin SELF
49 уровень , 4 лекция
Открыта

1. Ментальная модель JSON и XML

Если честно, разработчик редко просыпается утром с мыслью: «А не сравнить ли мне сегодня JSON и XML, пока кофе не остыл». Обычно выбор формата происходит потому, что кто-то другой уже сделал половину решения за вас: внешний сервис отдаёт XML, старое корпоративное API требует SOAP, бухгалтерия присылает «великолепный» XML-файл, а ваш проект, наоборот, хочет простой отчёт в JSON. И вот вы стоите между двумя мирами: компактным «данные как структура» (JSON) и более «документным» миром тегов (XML).

Важно понимать: JSON и XML — это не «лучше/хуже», а разные модели представления данных. И если выбрать формат без критериев, вы легко получите ситуацию, когда данные вроде бы передаются, но каждый следующий шаг разработки начинает напоминать квест «угадай, где мы потеряли типы / порядок / вложенность / экранирование».

Чтобы выбирать формат осознанно, полезно сначала договориться, как мы вообще мысленно «видим» данные в каждом формате. Это похоже на выбор контейнера для еды: суп в пакете технически возможен, но вы не обязаны так страдать. JSON и XML по-разному отвечают на вопрос «что такое структура данных», и это напрямую влияет на то, насколько легко нам будет читать, валидировать и генерировать эти данные в Kotlin.

JSON как дерево значений

JSON — это дерево, где узлы бывают четырёх основных типов: объект (пары ключ-значение), массив (упорядоченный список), примитив (строка/число/boolean) и null. В предыдущих лекциях мы буквально работали с этим как с деревом JsonElement, и это ощущение очень точное: каждый узел — значение, у которого есть тип, и тип можно проверить.

Практический эффект: если вы видите true или 123 в JSON, вы уже понимаете, что это boolean и число (пусть иногда и приходится приводить из строки, если данные «грязные»).

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, не экранировали &amp; или &lt; в XML, случайно добавили лишний перенос строки, и всё — парсер недоволен, пользователь недоволен, вы недовольны жизнью. Если есть возможность собирать JSON как дерево — делайте так. Для XML лучше тоже стремиться к структурному построению, но в рамках этой лекции мы хотя бы фиксируем проблему: строковая сборка хрупкая.

Ошибка №6: выбирать формат «потому что он нравится», а не потому что он подходит задаче.
Любить JSON — нормально. Любить XML — тоже нормально (главное, чтобы это не стало вашей единственной чертой характера). Но формат выбирают по критериям: что диктует интеграция, что проще валидировать, где важен порядок, где нужна компактность, кто будет читать данные — человек или программа. Если критерии не проговорены, решение обычно получается случайным, а цена случайности — время команды и качество продукта.

1
Задача
Kotlin SELF, 49 уровень, 4 лекция
Недоступна
Справка формата
Справка формата
1
Задача
Kotlin SELF, 49 уровень, 4 лекция
Недоступна
Двойная сериализация
Двойная сериализация
1
Задача
Kotlin SELF, 49 уровень, 4 лекция
Недоступна
Выбор формата
Выбор формата
1
Задача
Kotlin SELF, 49 уровень, 4 лекция
Недоступна
Возраст из текста
Возраст из текста
1
Опрос
JSON без схемы, 49 уровень, 4 лекция
Недоступен
JSON без схемы
JSON без схемы
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ