Всё начинается с сетевого запроса
Открываете DevTools, вкладка Network, перезагружаете страницу. Среди запросов к картинкам и шрифтам мелькает вызов к API. Кликаете — и в панели Preview разворачивается белая простыня текста: фигурные скобки, двоеточия, кириллица, местами превратившаяся в а — будто шифр, а не буквы. На первый взгляд это мусор. На второй — мусор с системой. Поле title, и рядом строка с названием статьи. Поле createdAt, и рядом дата. Вложенный объект author, а внутри — name, email, id. Вы ещё не знаете, как это называется, но уже умеете это читать.
Я почти уверен, что большинство читающих знакомо с JSON именно так — случайно, через замочную скважину инструментов разработчика. Формат настолько вездесущ, что им можно пользоваться годами, ни разу не задумавшись о том, как он устроен. Он в ответе каждого API, в каждом конфиге, в localStorage любого сайта. И вот парадокс: стандарт обмена данными, который четверть века держит на себе интернет, настолько прост, что о нём будто бы и писать нечего. Но за этой простотой прячется куча вещей, о которых спотыкаются даже опытные люди. Давайте по порядку.
Что такое JSON на самом деле
JSON — это JavaScript Object Notation, «объектная нотация JavaScript». Расшифровка не объясняет ровным счётом ничего тому, кто не знает JavaScript, и объясняет лишь часть тому, кто знает. В начале двухтысячных Дуглас Крокфорд — тогда уже заметная фигура в мире JavaScript — взял синтаксис, которым в этом языке описывают объекты, и вынес его в самостоятельный формат обмена данными. Идея до смешного простая: если в языке уже есть нотация, описывающая произвольную структуру данных, зачем изобретать вторую?
Получилось ровно то, что вы ожидаете: файл (или строка в теле HTTP-ответа), где данные записаны так, как их записал бы программист, не дожидаясь формальных спецификаций. Название, как я уже сказал, неудачное. Кроме родословной синтаксиса, у JSON с JavaScript нет ничего общего. Он не исполняется, не требует интерпретатора, не зависит от версии языка. Его можно разобрать на языке, в котором вообще нет понятия «объект», — например, на Python или Go. «Объектная нотация» прилипла к нему исторически и не отлипает.
При этом у формата есть редкое свойство: он читается и людьми, и машинами. Человек пробегает глазами, находит "password", видит рядом значение. Машина прогоняет текст через парсер и получает структуру данных. Обычно форматы выбирают одну сторону: бинарный протокол машина разбирает мгновенно, но человек в него смотреть не может; текстовый конфиг человек читает, но машине приходится повозиться. JSON умудряется сидеть посередине, и это большая часть его живучести.
Стандартизация, как водится, припоздала. Формат разошёлся по миру ещё до всяких официальных бумаг; зафиксировали его сначала в RFC 4627 (2006), затем в ECMA-404 (2013) и RFC 7159 (2014), а позже — в RFC 8259 (2017). Забавно, что стандарт всего лишь описал то, что и так работало повсеместно. В этом смысле JSON похож на метр или килограмм: определение появилось после того, как все уже давно договорились. Сайт json.org до сих пор выглядит как страница образца 2001 года, и это, пожалуй, самое честное описание сути, которое у нас есть.
Почему он победил XML
Сейчас это трудно представить, но в начале двухтысячных обмен данными между системами был занятием для терпеливых. Царствовал XML, а вместе с ним — SOAP-веб-сервисы. SOAP — это протокол, который берёт вызов функции и заворачивает его в XML-конверт, заворачивает конверт в другой конверт, а потом добавляет служебные заголовки. Чтобы вызвать метод с одним строковым параметром, вы отправляли сообщение на килобайты, в котором сами данные занимали тридцать символов, а остальное — обвязка. Сервисы описывались на WSDL — языке описания, который генерировался из кода и на который невозможно было смотреть без боли.
XML был педантичен и в мелочах. У вас постоянно был выбор: положить значение в элемент <id>1</id> или в атрибут <user id="1"> — и каждая библиотека делала это по-своему. В значениях надо было экранировать спецсимволы, в структуру приходилось добавлять пространства имён, и тогда каждый тег получал префикс, и файл становился похож на телефонный справочник, в котором перемешали страницы. А чтобы превратить это безобразие во что-то понятное программе, требовался XSLT — отдельный язык трансформаций, который было проще выучить, чем полюбить. Многие так и не выучили.
Я не буду утверждать, что JSON красив. Он не красив — это скобки и запятые. Он победил, потому что всё остальное было невыносимее. Вот он, наглядный контраст:
<user id="42">
<name>Анна</name>
<email>anna@example.com</email>
<roles>
<role>admin</role>
<role>editor</role>
</roles>
</user>
А вот тот же пользователь в JSON:
{
"id": 42,
"name": "Анна",
"email": "anna@example.com",
"roles": ["admin", "editor"]
}
Разница не в эстетике, а в отображении на структуры данных, которые есть в каждом языке программирования. Объект — это словарь, он же хеш-таблица, он же ассоциативный массив. Массив — это список. Плюс строка, число, булево значение и null. Всё, других типов нет, и этого достаточно. Для чтения JSON не нужна схема — вы читаете его, как читаете любой текст, потому что он записан почти как текст. Порог входа — минут пятнадцать, включая чашку чая. Я впервые увидел JSON в ответе какого-то форумного API в 2008-м и разобрался в нём, даже не заметив, что чему-то учусь. С XML такого не было никогда.
Шесть типов данных
Весь JSON стоит на шести типах. Их легко запомнить, потому что они в точности совпадают с тем, что уже есть в любом языке.
Объект — это набор пар «ключ — значение», заключённый в фигурные скобки:
{ "name": "Анна", "age": 31 }
Ключ всегда строка, значение — любой из шести типов, включая другой объект или массив.
Массив — упорядоченный список значений в квадратных скобках:
["admin", "editor", "viewer"]
Внутри могут быть любые типы, в том числе вперемешку, хотя на практике смешивать типы в одном массиве — почти всегда плохая идея.
Строка — последовательность символов в двойных кавычках:
"строка в двойных кавычках"
Число — целое, дробное, с экспонентой:
42
-3.14
6.022e23
Булево значение — true или false, и только так, маленькими буквами. True, TRUE, 1 — всё это в JSON не является булевым значением.
null — явное отсутствие значения.
null
Вот как всё это складывается в один реалистичный объект — например, в ответ API, отдающий пользователя:
{
"id": 42,
"name": "Анна Смирнова",
"email": "anna@example.com",
"active": true,
"lastLogin": null,
"roles": ["admin", "editor"],
"profile": {
"bio": "Фронтенд-разработчик",
"avatarUrl": "https://example.com/avatars/42.png"
}
}
Обратите внимание на "lastLogin": null — это не строка, не ноль и не пустой объект. Это способ сказать «значения нет», и это не то же самое, что «значение равно нулю». Разница между null и 0 или null и "" постоянно становится источником багов, потому что обе стороны интерпретируют отсутствие по-своему. Формат задаёт синтаксис, но не семантику — договорённости о том, что означает то или иное поле, остаются на совести людей, проектирующих API.
Острые углы синтаксиса
Правил в JSON мало, но они железные, и спотыкаются на них регулярно. Перечислю то, о чём стоит помнить.
Ключи — только в двойных кавычках. Это самое частое недоразумение у людей, пришедших из JavaScript, где объект можно писать без кавычек: {name: "Анна"} — валидный JS, но невалидный JSON. В JSON ключ без кавычек — ошибка сразу, как и ключ в одинарных кавычках.
Строки — только двойные кавычки. Одинарные кавычки допустимы во многих языках, но не в JSON. Любая строка на 'текст' будет отвергнута парсером.
Комментариев нет. Ни //, ни /* */, ни #. Файл — только данные. Это не ошибка дизайна, а следствие простоты: комментарии потребовали бы определить, что с ними делать при передаче между системами. Но конфиги от этого страдают, и об этом я ещё скажу.
Хвостовых запятых нет. После последнего элемента ни запятой, ни тем более висячей запятой. [1, 2, 3,] — это ошибка. Тут же кроется классическая боль: когда добавляете новый элемент в конец, надо не забыть поставить запятую после предыдущего.
В числах нет ведущих нулей. 042 недопустимо. -0 допустимо — это именно ноль, — а вот .5 без ведущего нуля нет; только 0.5. Чисел вроде NaN или Infinity не существует: это не числа с точки зрения JSON, а значит, и не числа в ваших данных, если вы хотите, чтобы они прошли через парсер.
Дублирующиеся ключи формально допустимы, но бессмысленны. {"a": 1, "a": 2} пройдёт валидацию, но стандарт говорит, что поведение парсера в этом случае не определено. На практике большинство парсеров просто оставляет последнее значение. Полагаться на это нельзя, но и сюрприза ждать не стоит.
Юникод. Символы за пределами ASCII можно записывать напрямую, а можно через \uXXXX. Для символов вне базовой плоскости (например, эмодзи) нужны суррогатные пары — две \uXXXX-последовательности подряд. Экранирование обязательно, если в строке встречается управляющий символ, — скажем, перевод строки.
Полный список управляющих последовательностей:
\" — двойная кавычка
\\ — обратный слэш
\/ — косая черта
\b — backspace
\f — перевод страницы
\n — перевод строки
\r — возврат каретки
\t — табуляция
\uXXXX — символ Unicode
Синтаксис JSON выучивается за один вечер, а тонкости перестают кусаться после пары ночей отладки.
Является ли JSON подмножеством JavaScript?
Вопрос, который периодически всплывает и каждый раз приводит к путанице. Короткий ответ: нет, не является. JSON похож на литерал объекта в JavaScript, но это два разных синтаксиса, которые совпадают лишь в простейших случаях.
Смотрите. Вот валидный JavaScript-объект:
{
name: 'Анна', // одинарные кавычки
role: 'admin',
}
Это не JSON: одинарные кавычки, отсутствие кавычек у ключа, комментарий, хвостовая запятая. А вот валидный JSON:
{"name": "Анна", "role": "admin"}
Это почти валидный JavaScript-объект — если не считать того, что это не литерал, а строка, которую надо разобрать. Разница кажется формальной, но она принципиальная. В объекте JavaScript можно хранить функции, undefined, NaN, Infinity, объект Date. Ничего из этого в JSON нет. NaN не является числом JSON, undefined не является ничем, функция — тем более.
История добавляет иронии. Первый RFC, описывающий JSON (RFC 4627, 2006 год), называл JSON подмножеством JavaScript. Это утверждение потом сочли неудобным: выяснилось, что есть строки, валидные в JSON, но не валидные в JavaScript того времени. Речь о символах-разделителях строк U+2028 и U+2029, которые в JSON-строках допустимы, а в литералах JavaScript когда-то ломали парсинг. Позже это поправили в самой спецификации языка, но сам факт показателен: то, что «почти» является подмножеством, на практике оказывается источником сюрпризов.
Зачем вообще это различать? Затем, что при работе с JSON.parse ожидания должны соответствовать реальности. Функция JSON.parse в JavaScript — строгий парсер: он принимает только валидный JSON и бросает исключение на любом отклонении. Он не обязан принимать то, что валидно как литерал. Писать JSON.parse("{ name: 'Анна' }") — значит получить SyntaxError, потому что это не JSON. Понимание границы между двумя синтаксисами экономит время, которое иначе уходит на недоумённый поиск в интернете.
Где вы встречаете его каждый день
JSON настолько вездесущ, что перечислять все места его обитания — задача на целый день. Начну с самого очевидного.
REST API. Тело запроса и тело ответа — это почти всегда JSON. GET /users/42 возвращает объект пользователя, POST /login принимает объект с email и password. Современные интерфейсы — от погодных сервисов до банковских шлюзов — говорят на JSON.
Конфигурационные файлы. package.json в каждом Node.js-проекте — это JSON. tsconfig.json, composer.json, manifest.json для веб-приложений, конфиги бесчисленных утилит — всё это JSON. Если у вас был проект на JavaScript, вы правили package.json руками и, скорее всего, не раз. Некоторые из таких файлов — не строгий JSON: например, tsconfig.json (формат JSONC) допускает комментарии, а вот package.json или composer.json — строгий JSON.
Хранилища браузера. localStorage принимает только строки, поэтому разработчики привыкли сериализовать туда объекты через JSON.stringify, а читать через JSON.parse. Та же история с IndexedDB, где JSON — распространённый способ упаковать запись.
Базы данных. PostgreSQL давно умеет работать с типом jsonb — хранить документ, индексировать по его полям и запрашивать их прямо из SQL. MongoDB хранит документы в BSON — бинарном варианте JSON, который добавляет к базовым шести типам ещё и даты, ObjectId и буферы. Даже реляционные базы вроде MySQL и SQLite в какой-то момент обзавелись JSON-типами, потому что миру это было нужно.
JSON Lines. Он же NDJSON — формат, где каждая строка файла это отдельный JSON-объект. Используется для логов, потоковых данных, бэкапов: можно читать построчно, не загружая весь файл, а обрыв в середине не убивает всё остальное. Если вы видели файлы .jsonl или .ndjson — это он.
WebSocket. Сообщения в веб-сокетах в подавляющем большинстве случаев — это JSON-строки. Транспорт сверху не важен, формат данных — вот он.
Браузерный fetch. response.json() — метод, который мы вызываем не задумываясь. Он разбирает тело ответа в JavaScript-значение, и это, пожалуй, самый частый контакт обычного разработчика с JSON за день.
На каждый из этих пунктов можно написать отдельный пост, но суть одна: формат стал настолько стандартным, что его используют по умолчанию, без обсуждения. Это и есть признак победившего стандарта — его не выбирают, его подразумевают.
Как его разбирают в разных языках
У каждого языка — своя манера обращаться с JSON, и в каждом есть свои грабли. Пара беглых зарисовок, без попыток написать учебник.
JavaScript. Здесь всё нативно. JSON.parse(str) превращает строку в значение, JSON.stringify(obj) — наоборот. Это встроенные функции, и они строгие: parse бросает SyntaxError на невалидном входе, а stringify молча пропускает функции и undefined в объектах. Если вы работаете с браузером, вы уже умеете разбирать JSON — вы делаете это каждый день.
Python. Модуль json из стандартной библиотеки: json.loads и json.dumps. Отличительная черта — loads по умолчанию превращает JSON-объекты в словари dict, а dumps по умолчанию не умеет сериализовать пользовательские классы и множества (например, set) — приходится передавать default-функцию. Классическая ловушка — числа: большие целые в loads приходят как int, но если передать парсеру флаг, они легко превращаются во float с потерей точности.
Java. Здесь JSON не встроен в язык, так что все пользуются библиотеками. Jackson и Gson — два самых известных имени. Jackson силён в маппинге на классы, но требует аннотаций и осторожности с дженериками; Gson проще, но и менее гибкий. В обоих случаях привычная боль — сериализация дат и обработка неизвестных полей: поведение по умолчанию у библиотек разное, и его приходится явно настраивать.
Go. Пакет encoding/json из стандартной библиотеки: json.Marshal и json.Unmarshal. Отличительная черта — работа с тегами: поля структур помечаются как json:"name", и это дисциплинирует: без тега поле называется так же, как в Go (с большой буквы), а это почти наверняка не то, что ожидает ваш API. Ещё нюанс — Unmarshal молча игнорирует неизвестные поля, а числа по умолчанию приходят как float64, что ломает большие целые.
Rust. В стандартной библиотеке JSON нет — экосистема давно выбрала serde. Это не библиотека, а целая система: вы описываете структуры, помечаете поля атрибутом #[derive(Serialize, Deserialize)], и сериализация работает почти без участия разработчика. Цена — порог входа: поначалу система атрибутов и генерируемый код кажутся магией, а сообщения об ошибках — лаконичными до злости. Но когда вникаешь, обратно уже не хочется.
Во всех языках история повторяется: JSON прост как формат, но полон нюансов как реализация. Типичные грабли — числа, даты, отсутствующие поля — не зависят от языка и в каждом возвращаются в новой обёртке.
Безопасность: уроки, оплаченные кровью
У JSON тёмная история с безопасностью, и она поучительна. Начну с самого известного урока.
В начале двухтысячных, когда формат только распространялся, распространённым (и чудовищным) приёмом было разбирать JSON через eval(). Логика была «простая»: раз JSON — это почти литерал JavaScript, то eval(str) вернёт нужный объект. Работало отлично ровно до тех пор, пока в строке не оказывался код. eval('({"admin": true})') — это исполнение произвольного кода в вашем процессе. Если JSON пришёл из недоверенного источника, а он почти всегда приходит откуда-то извне, то eval — это открытая дверь для атаки. В ответ на это Крокфорд написал библиотеку json2.js со строгим парсером, который разбирал текст, не исполняя его. Урок был оплачен кровью — но усвоен.
Дальше был JSONP. Старый трюк для обхода ограничений браузера на междоменные запросы: сервер оборачивал JSON в вызов функции — callback({...}), — а страница подключала это как <script>. Работало, но открывало дыры: любой скрипт, подключённый таким образом, исполняется в контексте вашей страницы и имеет доступ ко всему, что есть у вашего домена. JSONP до сих пор встречается в старых API, но в новом коде его быть не должно.
Была и другая история — угон JSON через массив верхнего уровня (JSON hijacking). Страница могла подключать чужой API-ответ как <script>, а тот начинался с [. В старых браузерах этого хватало: атакующий переопределял конструктор Array или методы прототипа, и при исполнении скрипта данные из массива тихо утекали в его код. Обходной путь был простым и грубым: требовать, чтобы корень ответа был объектом — {"data": [...]}, — а не массивом. В спецификацию это требование никогда не попадало: первый RFC 4627 разрешал в корне документа объект или массив, а RFC 7159 и RFC 8259 сняли и это ограничение — сегодня корнем JSON может быть любое значение, хоть голое число 42. В современных браузерах сама атака не работает: семантику литералов массива переписали ещё в ES5, и подмена конструктора больше ни на что не влияет. Но привычка заворачивать данные в объект осталась, как и оборонительные приёмы вроде )]}' перед полезной нагрузкой.
Позже пришла prototype pollution через ключи __proto__. Если ваша библиотека сливает разобранный JSON в объект без проверок, а в JSON есть {"__proto__": {"isAdmin": true}}, то поля могут «утечь» в прототип и отравить объекты всего приложения. Современные библиотеки и движки закрывают большую часть этих дыр, но правило остаётся: не сливайте JSON в объекты бездумно и не доверяйте ключам, которые выглядят как служебные.
Отдельная глава — JSON-бомбы: глубоко вложенные документы, которые при разборе заставляют парсер рекурсивно углубляться и либо съедают стек, либо память. Скажем, тысяча вложенных друг в друга массивов — и стек вашего парсера может кончиться. Плюс огромные полезные нагрузки, которые качаются и разбираются без ограничений.
Современное правило звучит скучно, но оно избавило многих от бессонных ночей: всегда использовать настоящий парсер и никогда eval; ограничивать размер входящих данных; выбирать библиотеки, которые не сливают данные в прототипы; и не забывать, что JSON — это текст, а не код. Цену этого забывания мир уже платил в эпоху eval и JSONP.
Проверка и расширения: Schema, JSON5
JSON описывает синтаксис, но не говорит, какие поля в объекте обязательны, какие бывают типы и какого диапазона числа. За это отвечает JSON Schema — отдельная спецификация (она живёт на json-schema.org), позволяющая описать ожидаемую структуру документа и проверять документы на соответствие. Выглядит это примерно так:
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 }
}
}
Это способ внести в мир JSON дисциплину, которой в самом формате нет. Используется в контрактах API, в валидации конфигов, в документации. Но это отдельная, самостоятельная система, и многие ею не пользуются вовсе — вольница JSON в этом смысле и удобство, и проклятие.
Куда интереснее — расширения, которые люди придумали потому, что строгий JSON им неудобен. Самое живое из них — JSON5. Это надмножество JSON, разрешающее то, что запрещено в оригинале: комментарии, хвостовые запятые, ключи без кавычек, одинарные кавычки, Infinity, NaN и числа в шестнадцатеричной записи. По сути, JSON5 возвращает ту «человекочитаемость», которую строгий JSON у себя выжег. Пользуются им в основном для конфигов, где важна ручная правка. Минус очевиден: это не стандарт, и никакой инструмент, ожидающий строгий JSON, ваш JSON5 не поймёт — нужен отдельный парсер.
Есть и JSONC — «JSON with Comments». Это не спецификация, а скорее соглашение: JSON, в котором разрешены комментарии. Его понимают редакторы вроде VS Code (файлы settings.json с комментариями — это как раз JSONC), и многие тулзы. По сути, та же идея, что у JSON5, но минимальная: только комментарии.
Бывают и более экзотические варианты, вроде HJSON, который разрешает ещё больше вольностей. Все они — прагматичные хаки, а не стандарты. Каждый жертвует совместимостью ради удобства человека, и каждый требует, чтобы вы следили, какой именно «JSON» вы пишете. Мне это напоминает вечный спор двух лагерей: тех, кто любит строгий JSON, и тех, кто пишет его с комментариями, а потом долго объясняет, почему их конфиг «не работает».
Его ограничения и претензии
Справедливости ради — претензий к JSON накопилось достаточно. Начну с самой известной.
Нет комментариев. Для обмена данными это почти достоинство: комментарии не нужны машинам и только создают путаницу. Но для конфигурационных файлов это боль. В package.json невозможно объяснить, почему стоит именно эта версия зависимости, — приходится выносить объяснения в README или мириться с их отсутствием. Отсюда и весь зоопарк JSON5/JSONC.
Нет типа «дата». Дата в JSON — это строка. И каждый сам решает, как её писать. Хорошо, если ISO 8601 — "2026-08-12T14:30:00Z", — но часто это локальные форматы, timestamp-числа или вообще «как сложилось». Библиотеки вроде date-fns или Joda существуют в том числе потому, что кто-то должен разбирать весь этот хаос. Формат не виноват, но страдают все.
В большинстве популярных парсеров числа реализованы как IEEE-754 double. Точность ограничена: числа с плавающей точкой теряют точность на больших значениях, а целые длиннее 2^53 (примерно 9 007 199 254 740 992) уже не гарантированно представимы. Банковские системы, оперирующие копейками и крупными суммами, чувствуют это на себе: сумма в 9999999999999999 может приехать не той, которой была. Отсюда привычка передавать деньги строками и считать в целых единицах мельче рубля.
Нет бинарных данных. Строки в JSON — это текст, а значит, картинку или файл приходится передавать в base64, что раздувает данные примерно на треть. Для метаданных это нормально, для больших файлов — издевательство. Отсюда отдельные протоколы и форматы для бинарного потока.
Глубокая вложенность может ронять парсер. Рекурсивный парсер с вложенными структурами может переполнить стек. На практике редко кто вкладывает объекты на глубину в тысячу уровней, но атаки это используют — см. раздел про безопасность.
Дубликаты ключей. Как я уже говорил, поведение не определено. Два парсера могут разобрать один и тот же документ по-разному, и это источник тонких багов в распределённых системах, где документ проходит через несколько сервисов.
Изобретение велосипедов. Формат не навязывает, как представлять деньги, даты, ошибки, пагинацию, статусы. Каждый дизайнер API решает это заново. Отсюда бесконечные споры о том, как должен выглядеть «правильный» ответ с ошибкой и сколько полей должно быть в объекте пагинации. JSON не виноват — виновата его вольница. Но вольница эта и есть источник половины его претензий.
Альтернативы
Честно говоря, если вам неудобен JSON, есть из чего выбрать. Вот короткий обзор, без фанатизма.
YAML — самый «человеческий» из текстовых форматов. Вместо скобок — отступы, вместо запятых — переводы строк. Читается отлично, конфиги на нём писать одно удовольствие. Но печально известен своей хитростью: отступы решают всё, табуляции нельзя смешивать с пробелами, а парсеры по-разному интерпретируют одни и те же значения (знаменитая история про то, что on, yes и no в некоторых реализациях превращаются в булевы значения). Хорош для конфигов, опасен для данных, которые кто-то будет вводить руками в большом объёме.
TOML — формат, спроектированный специально для конфигурации. Секции, пары «ключ = значение», никакой магии с отступами. Легко читается, легко пишется, предсказуем. Разумный выбор для новых конфиг-файлов. Но он не про обмен данными между системами — для этого не годится.
Protocol Buffers (protobuf) — бинарный формат от Google. Компактный, типизированный, быстрый. Но требует схемы (.proto-файлов) и генерации кода на каждом языке. Если у вас микросервисы, которые говорят друг с другом внутри одной инфраструктуры, и вы готовы платить за генерацию кода, — это мощный инструмент. Если у вас публичный API, куда стучатся чужие клиенты, — это обуза.
MessagePack и CBOR — бинарные варианты JSON. Идея в том, чтобы взять ту же структуру (объекты, массивы, строки, числа), но закодировать её компактнее. Плюс — скорость и размер; минус — нечитаемость для человека и свои грабли с типами. Полезны во встраиваемых системах, в высоконагруженных внутренних протоколах, в WebSocket-транспорте, когда каждый байт на вес золота.
BSON — бинарный JSON от MongoDB. Добавляет типы (дату, ObjectId, буфер) к базовым шести. Хорош ровно там, где MongoDB: внутри базы данных. Наружу его тащить незачем.
Выбор честно зависит от задачи. Для обмена между браузером и сервером альтернатив JSON, по сути, нет — браузер говорит на JSON нативно. Для внутренних сервисов с жёсткими требованиями к размеру и скорости — protobuf или MessagePack. Для конфигов — TOML или YAML, если знаете, что делаете. JSON остаётся тем, чем был: универсальным клеем, который работает везде, где есть текст.
Заключение: скучные форматы живут дольше
Прошло больше двадцати пяти лет с тех пор, как Крокфорд вытащил синтаксис из JavaScript. За это время сменилось несколько поколений модных технологий, а JSON всё ещё здесь — в каждом запросе, в каждом конфиге, в каждой базе данных. Почему? Ответ скучный и точный: достаточно хорош, доступен везде, до смешного прост. Ему не нужно быть красивым, ему нужно быть предсказуемым — и он предсказуем.
Есть что-то поучительное в том, как побеждают такие форматы. Они не выигрывают в конкурсах элегантности. Их не продвигают маркетинговыми кампаниями. Они просто достаточно удобны, чтобы с ними можно было жить, и достаточно просты, чтобы их можно было реализовать на любом языке за пару дней. Когда приходит очередной «лучший формат», выясняется, что ради его преимуществ надо жертвовать совместимостью — и большинство предпочитает не жертвовать. Умные люди уже не одно десятилетие учат нас: в инфраструктуре выигрывает не самое красивое, а самое терпимое.
Если вам понадобится выбрать формат для новой системы — выбирайте с умом. Для конфига, который будут читать люди, подумайте о TOML. Для внутреннего протокола с жёсткими требованиями — о protobuf. Но для всего остального, вероятно, подойдёт JSON: он уже там, где вы собираетесь его использовать, и вы умеете с ним работать ещё до того, как начали. Иногда правильный инженерный выбор — это просто не изобретать новый формат и спокойно записать данные в скобки и запятые.
