JSON или XML: формат, схема и контракт получателя — выбор диктует принимающая сторона. Ниже один набор из трёх записей в обоих синтаксисах, три контракта A/B/C с заполненным решением, полная JSON Schema и проверки, где строковый id, null и отсутствие note дают разные исходы.
Полный JSON и XML для одних трёх записей
Возьмём каталог: id=1, name=Клавиатура, note=беспроводная; id=2, name=Мышь, note отсутствует; id=3, name=Монитор 27″, note=4K. Порядок записей сохраняется. Отсутствие note отличается от пустой строки и от null — это три разных состояния, и получатель может различать их по-разному.
Полный JSON:
{"items":[{"id":1,"name":"Клавиатура","note":"беспроводная"},{"id":2,"name":"Мышь"},{"id":3,"name":"Монитор 27\"","note":"4K"}]}
Полный XML:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item id="1"><name>Клавиатура</name><note>беспроводная</note></item>
<item id="2"><name>Мышь</name></item>
<item id="3"><name>Монитор 27"</name><note>4K</note></item>
</items>
В RFC 8259 описано, что JSON представляет четыре примитивных типа (строки, числа, булевы значения, null) и два структурных (объекты и массивы); массив — упорядоченная последовательность значений, объект — неупорядоченная коллекция пар имя/значение. В обычном текстовом содержимом XML символы & и < экранируют; спецификация XML 1.0 от W3C отдельно допускает их буквальный вид в комментариях, инструкциях обработки и CDATA. Двойная кавычка внутри текстового узла допустима. Поэтому в JSON завершающая кавычка в «Монитор 27″» экранируется обратным слэшем, а в XML-тексте стоит как есть. Обе строки после парсинга дают одинаковое имя.
Решения A/B/C по контрактам получателей
Контракт получателя решает выбор. Ниже три учебных потребителя с явными условиями.
| Получатель и обязательный контракт | Выбор | Проверка приёмки |
|---|---|---|
| A: принимает JSON-объект items и проверяет JSON Schema; id — integer, note может отсутствовать, null запрещён | JSON | Исходный набор проходит; id=»2″ и note=null не проходят; отсутствие note проходит |
| B: существующий импорт принимает корневой items и повторяющийся item с атрибутом id, дочерними name и необязательным note; JSON он не принимает | XML | Структура соответствует контракту; id из атрибута преобразуется приложением в целое; у второй записи note отсутствует |
| C: оба формата принимаются, схема и комментарии не требуются, приложение работает с объектами и числовым id | JSON как первый кандидат | После парсинга id остаётся числом; решение меняют, если появится обязательный XML-контракт |
Для A формат задан схемой и типом id. Для B формат задан существующим импортом: даже компактная JSON-сериализация нарушит контракт, потому что импорт ожидает корневой items и повторяющийся item. Для C оба формата равноправны, и первый кандидат — JSON, поскольку приложение работает с объектами и числовым id. Это условия учебных потребителей. Принимаемый формат важнее общей репутации формата.
Полная JSON Schema A и три исхода проверок
Схема для получателя A:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["items"],
"additionalProperties": false,
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "name"],
"additionalProperties": false,
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"note": {"type": "string"}
}
}
}
}
}
В документации JSON Schema на json-schema.org сказано: свойства объекта описываются ключевым словом properties, а additionalProperties управляет лишними полями — значение false запрещает дополнительные свойства. По умолчанию пропуск свойства допустим, и это описано в разделе про required. Отсюда три исхода на нашем наборе:
- id=»2″ (строка) отклоняется: схема требует integer.
- note=null отклоняется: схема допускает строку, а null — отдельный тип.
- Отсутствие note проходит: свойство не входит в required.
Проверяется тип и наличие. Синтаксически корректный JSON с id-строкой остаётся невалидным по схеме.
Имена, порядок и id после преобразования XML-атрибута
Сравним нормализованные значения. Эталон после парсинга: три записи, id=[1,2,3]; имена совпадают посимвольно, включая завершающую кавычку в «Монитор 27″»; у первой note=беспроводная, у второй элемента note нет, у третьей note=4K.
В XML обычный парсер возвращает атрибут id строкой. Для смыслового сравнения приложение явно делает int(id). Выдавать текст атрибута за типизированное число без схемы или такого правила приложения нельзя. В JSON число 1 остаётся числом после парсинга.
Порядок: массив JSON упорядочен, и последовательность id=[1,2,3] сохраняется. В XML порядок дочерних элементов item также задаёт последовательность. Атрибуты внутри одного тега упорядоченными не считаются — спецификация XML прямо говорит, что порядок спецификаций атрибутов в стартовом теге незначим. Поэтому id как атрибут и id как дочерний элемент дают разный доступ к значению.
Отсутствие note нельзя приравнивать к пустой строке или null. Подстановка пустой строки меняет вход, даже если приложение её принимает. Проверка 2 фиксирует: у второй записи элемента note действительно нет.
JSON Schema и XSD: строгая проверка в обоих форматах
Строгая проверка доступна в обоих форматах. JSON Schema задаёт типы, обязательные свойства и запрет лишних полей. XSD, по странице W3C XML Schema, выражает общие словари и позволяет машинам выполнять правила, заданные людьми: структуру, содержимое и семантику XML-документов. XML Schema 1.0 стала рекомендацией W3C 2 мая 2001 года, вторая редакция с исправлениями опубликована 28 октября 2004 года; XML Schema 1.1 также является рекомендацией и задумана совместимой с 1.0.
| Критерий | JSON Schema | XSD |
|---|---|---|
| Типы значений | integer, string, number, boolean, null, object, array | типы задаются схемой и приложением |
| Обязательные поля | ключевое слово required | объявление элемента и атрибута |
| Лишние поля | additionalProperties: false | ограничения объявления |
| Комментарии в данных | стандарт JSON их не включает | XML допускает комментарии |
Наличие строгой валидации само по себе не выбирает XML. Оба инструмента закрывают задачу проверки типов и обязательности. Выбор идёт от контракта получателя: если импорт принимает XML, XSD описывает его структуру; если принимающая сторона проверяет JSON Schema, схема задаёт те же ограничения.
REST, размер и что не стоит обещать
REST не требует JSON. Формат тела определяет API, и учебные контракты выше показывают три разных ответа на одном наборе данных. В учебных контрактах выше формат задан требованиями каждого получателя.
Размер тоже не даёт универсального победителя. Конкретные JSON- и XML-сериализации имеют разный размер, и сравнение требует одинаковых данных, настроек сериализации, кодировки и сжатия. На тысячах записей выигрыш одного формата над другим зависит от этих условий, поэтому обещать его заранее нельзя.
По RFC 8259, JSON-текст, передаваемый между системами вне закрытой экосистемы, обязан кодироваться в UTF-8, а спецификация не требует добавлять BOM в начало сетевого JSON-текста. Это ограничение формата.
Ручной эталон, локальная проверка и запуск сервиса
Разделяйте три уровня проверки. Ручной эталон — это составленный человеком набор ожидаемых значений: три записи, id=[1,2,3], имена посимвольно, note у первой и третьей, отсутствие note у второй. Он не зависит от парсеров и служит точкой сравнения.
Локальная проверка парсеров — это прогон обоих документов через библиотеки на вашей машине: сравнить имена, порядок id и наличие note. Здесь выявляются различия в экранировании и в типе атрибута. Если локальные парсеры проверены дополнительно, отделяйте это от проверки работающего API.
Реальный запуск сервиса — это отправка набора в конкретный принимающий сервис и проверка его ответа. Результат зависит от версии сервиса, его схемы и настроек. Учебный пример выше — условный вход. Эталоны составлены вручную.
Как проверить выбор на одном пограничном наборе
Проверку удобно вести от ожидаемого результата. Сначала выпишите, что должно получиться после разбора: три записи в исходном порядке, числовые идентификаторы, имена посимвольно и разное состояние поля note у второй записи. Такой ручной эталон не зависит от библиотек и остаётся точкой сравнения, когда парсеры дают расхождения.
Дальше прогоните оба документа через местные библиотеки и сравните результат с эталоном. Здесь чаще всего и возникает ошибка: значение атрибута id приходит строкой, и при неаккуратном сравнении запись с id=»2″ либо выпадает из выборки, либо сортируется иначе. Приложение, которое по контракту приводит атрибут к целому, обязано делать это явно; иначе текст атрибута выдаётся за число.
Отдельно проверьте вторую запись. Если в разборе появился ключ note со значением null или пустой строкой, вход изменился: получатель увидит другое состояние. Полезно сравнить число записей и наличие поля до и после преобразования.
Наконец, отправьте набор конкретному получателю и сверьте его ответ с эталоном. Локальная проверка библиотек и ответ работающего сервиса — разные уровни: расхождение между ними говорит о настройках или версии принимающей стороны. Учебный набор выше остаётся условным входом.
Частые вопросы
Что выбрать для нового API — JSON или XML?
Начинайте с контракта получателя. Если принимающая сторона проверяет JSON Schema и работает с объектами и числовым id, подходит JSON. Если существующий импорт ожидает корневой items и повторяющийся item с атрибутом id, подходит XML. Общая репутация формата здесь не решает.
Почему отсутствие note не равно null?
Это разные состояния. В JSON Schema свойство note описано как строка; null — отдельный тип и отклоняется. Отсутствие свойства проходит, потому что note не входит в required. Подстановка null или пустой строки меняет вход и может изменить поведение получателя.
Как XML-парсер возвращает id из атрибута?
Обычный парсер возвращает значение атрибута строкой. Чтобы сравнить с числовым id из JSON, приложение явно преобразует int(id). Выдавать текст атрибута за типизированное число без схемы или правила приложения нельзя.
Можно ли проверить JSON строгой схемой?
Да. JSON Schema задаёт типы, обязательные свойства и запрет лишних полей через additionalProperties: false. По умолчанию пропуск свойства допустим, а лишние свойства разрешены. Включение required и additionalProperties делает проверку строгой.
Компактнее ли JSON на больших наборах?
Универсального выигрыша нет. Размер зависит от данных, настроек сериализации, кодировки и сжатия. Сравнивать нужно на одинаковых данных и одинаковых настройках. Выбор формата идёт от контракта получателя, а размер оценивают отдельно.
