TOML vs JSON vs YAML: какой формат конфигурации выбрать?
Вот краткая версия сравнения TOML vs JSON vs YAML: JSON — стандарт машинного обмена, который разбирает любой язык; YAML — дружелюбный к человеку формат, на котором работают Kubernetes и CI-конвейеры; а TOML — явный типизированный формат, созданный для конфигурации инструментов и приложений вроде Cargo.toml и pyproject.toml. Ни один формат не побеждает везде. Подходящий формат файла конфигурации зависит от трёх вещей: кто его редактирует, нужны ли комментарии и насколько строгими должны быть типы.
Простое практическое правило. Если файл пишет машина и читает машина — берите JSON. Если человек ежедневно правит вручную глубоко вложенную инфраструктуру — YAML оправдывает свою сложность. Если нужна очевидная типизированная конфигурация, в которой трудно ошибиться, — TOML самый безопасный выбор. Эти форматы вообще не конкуренты; они занимают разные полосы: JSON — для машин, YAML — для DevOps, TOML — для конфигурации инструментов. Дальше всё это разобрано на примерах бок о бок, на различиях, которые порождают баги, и на матрице выбора для следующего проекта.
Ответ за 30 секунд
Если у вас всего полминуты — вот та самая таблица сравнения форматов файлов конфигурации, за которой вы пришли.
| JSON | YAML | TOML | |
|---|---|---|---|
| Комментарии | Нет | Да (#) | Да (#) |
| Типы данных | Явные, минимальные | Неявные (выводятся по форме) | Явные, богатые |
| Нативные даты | Нет | Зависит от версии | Да (четыре типа) |
| Стиль вложенности | Фигурные скобки {} | Отступы | Заголовки [section] |
| Чувствительность к отступам | Нет | Да (табы запрещены) | Нет |
| Многострочные строки | Нет (только \n) | Да (| и >) | Да (""") |
| Висячие запятые | Запрещены | Неприменимо | Разрешены в массивах |
| Надмножество JSON | — | Да (1.2) | Нет |
| Лучше всего для | API, обмен данными | K8s, CI, Ansible | Конфигурация инструментов Rust/Python |
Из этой таблицы следуют три однострочных вывода:
- Выбирайте JSON, когда файл создают и читают программы: payload API,
package.json,tsconfig.json— всё, что пересекает границу системы. - Выбирайте YAML, когда люди правят вручную глубоко вложенную конфигурацию и экосистема этого уже ожидает: манифесты Kubernetes, GitHub Actions, Docker Compose, плейбуки Ansible.
- Выбирайте TOML, когда нужна понятная типизированная конфигурация инструмента или приложения — в основном плоская, с несколькими секциями, редактируемая участниками разного уровня:
Cargo.toml,pyproject.toml, Hugo, Netlify.
Понять, почему эти выводы верны, важно — ведь именно в причинах прячутся настоящие баги.
Одна конфигурация — три способа записи
Проще всего показать разницу на одной конфигурации, записанной трижды. Вот небольшая конфигурация сервиса: имя и версия, пара флагов верхнего уровня, список возможностей и вложенный блок базы данных.
JSON:
{
"name": "acme-api",
"version": "2.4.0",
"debug": false,
"released": "2026-07-02",
"features": ["auth", "metrics", "tracing"],
"database": {
"host": "db.internal",
"port": 5432,
"max_connections": 100
}
}
YAML:
name: acme-api
version: "2.4.0"
debug: false
released: "2026-07-02"
features:
- auth
- metrics
- tracing
database:
host: db.internal
port: 5432
max_connections: 100
TOML:
name = "acme-api"
version = "2.4.0"
debug = false
released = "2026-07-02"
features = ["auth", "metrics", "tracing"]
[database]
host = "db.internal"
port = 5432
max_connections = 100
Все три описывают одни и те же данные. Разница — в том, за что цепляется взгляд. Больше всего пунктуации в JSON: каждый ключ в двойных кавычках, каждый уровень добавляет фигурные скобки, а одна лишняя висячая запятая — уже синтаксическая ошибка. YAML убирает почти всё это, поэтому структура задаётся одними отступами — читается прекрасно, пока не проберётся таб. TOML посередине: кавычки только у строк, пары key = value и заголовок [database], который называет вложенный блок так же, как это делал бы старый INI-файл. Заметьте, что поле released во всех трёх записано строкой в кавычках, чтобы данные здесь оставались идентичными. Это сделано намеренно, ведь даты — как раз то место, где эти форматы перестают соглашаться друг с другом, и об этом стоит поговорить дальше.
JSON — стандарт машинного обмена
JSON — формат, к которому тянешься не задумываясь, и обычно этот инстинкт верен. В каждом популярном языке есть парсер, каждый HTTP API его понимает, а грамматика достаточно мала, чтобы держать её в голове. Он строгий и однозначный: строка есть строка, 5432 — число, true — булево значение, и каждое из них записывается ровно одним способом. Именно эта строгость делает JSON безопасным для передачи между системами, которые никогда друг о друге не знали. Когда сервис на Node отправляет JSON сервису на Go, оба согласны о смысле байт в байт.
Та же жёсткость объясняет, почему JSON доминирует на своей территории. package.json, tsconfig.json и composer.json — это JSON, потому что инструменты постоянно их создают и переписывают, а машинно-генерируемой конфигурации нужен машинно-строгий формат. Для payload API и очередей сообщений просто нет причин выбирать что-то другое.
Все слабости JSON проявляются в тот момент, когда его приходится править вручную. Нет комментариев. Нет висячих запятых, поэтому переупорядочить список — значит поправить запятые. Нет многострочных строк, только escape-последовательности \n. Каждому ключу нужны двойные кавычки. И нет типа даты, поэтому 2026-07-02 вынужден существовать как строка в кавычках и разбираться по договорённости. Для файла конфигурации, который сопровождает человек, эти пробелы складываются в постоянное трение.
Проблема «в JSON нет комментариев»
Самая востребованная функция JSON — это как раз то, что Дуглас Крокфорд намеренно оставил за бортом: комментарии. Его логика была в том, что комментарии подталкивают людей протаскивать директивы разбора в файлы конфигурации, ломая совместимость. Как бы вы к этому решению ни относились, простой JSON-конфиг нельзя аннотировать, а это болезненно для всего, что сопровождают люди. Практическое решение — надмножество. JSON5 добавляет комментарии, висячие запятые, ключи без кавычек и одинарные кавычки; JSONC (JSON with Comments) — более лёгкий вариант, который VS Code использует для своих настроек. Оба сохраняют форму JSON, делая его редактируемым. Если комментарии нужны, но хочется остаться в семействе JSON, прочитайте наше руководство по форматированию JSON5 и JSONC — о том, когда использовать каждый и как не расстроить инструментарий.
YAML — дружелюбный к человеку стандарт DevOps
YAML оптимизирован под человека за клавиатурой. Он поддерживает комментарии, его основанная на отступах раскладка держит глубоко вложенные структуры чистыми, а якоря позволяют определить блок один раз и переиспользовать его через псевдоним вместо копирования:
defaults: &defaults
timeout: 30
retries: 3
staging:
<<: *defaults
host: staging.internal
production:
<<: *defaults
host: prod.internal
Якорь &defaults называет блок, а <<: *defaults вливает его в обе среды, так что изменение общего timeout происходит в одном месте. Ни у JSON, ни у TOML нет ничего подобного. Именно эта читаемость сделала YAML лингва франка операционной работы: манифесты Kubernetes, workflow-файлы GitHub Actions, файлы Docker Compose и плейбуки Ansible — всё это YAML. Когда конфигурация уходит на пять уровней в глубину и человек правит её ежедневно, скупая пунктуация YAML — настоящее облегчение.
Цена — сложность и неожиданности. Спецификация YAML 1.2 велика, полное её соблюдение — редкость, поэтому разные парсеры расходятся на краях. Отступы значимы, табы запрещены полностью, а сдвинутый пробел может изменить смысл или сорвать разбор. Самый острый угол — неявная типизация: YAML угадывает тип скалярного значения без кавычек по его форме и ошибается достаточно часто, чтобы заслужить прозвище.
Проблема Норвегии в одном абзаце
Напишите country: NO в YAML — и многие парсеры вернут вам булево false, а не строку "NO", потому что YAML 1.1, которому по-прежнему следует большинство инструментов Kubernetes, трактует NO, YES, ON, OFF, Y и N как булевы значения. Код страны Норвегии превращается в false — молча, без ошибки. Та же неявная типизация превращает 0755 в восьмеричное число, а 1.20 — в 1.2. Решение скучное, но надёжное: заключайте в кавычки строки, которые можно принять за что-то другое. Эти грабли достаточно знамениты, чтобы заслужить отдельный разбор — с реальными сбоями и историей противостояния YAML 1.1 и 1.2 — в нашем руководстве по проблеме Норвегии в YAML. Для этой статьи вывод прост: удобство YAML и его опасность растут из одной и той же возможности.
YAML побеждает, когда люди ежедневно правят глубоко вложенную конфигурацию, а окружающая экосистема этого уже требует. Если вы пишете Helm-чарт, вы пишете YAML — и это нормально.
TOML — явная конфигурация, созданная для инструментов
TOML (Tom’s Obvious, Minimal Language, созданный Томом Престоном-Вернером) задумывался как тот формат конфигурации, о котором мечтали пользователи YAML: читаемый, но без угадывания. Его определяющая черта — явность. Типы однозначны, поэтому port = 5432 — всегда целое число, а name = "acme" — всегда строка, и здесь не на чем споткнуться из-за вывода по форме. Синтаксис TOML заимствует заголовок [section] из INI-файлов, что делает верхний уровень конфигурации легко просматриваемым, и он не чувствителен к отступам, поэтому пробелы никогда не меняют смысл. Спецификация TOML 1.0.0 маленькая и стабильная, и это преимущество: меньше шансов что-то забыть или перепутать.
Главная возможность TOML — нативные типы даты и времени, с которыми ни JSON, ни обычный YAML не справляются чисто. Их четыре: дата-время со смещением и часовым поясом (1979-05-27T07:32:00Z), локальные дата-время без пояса, локальная дата (1979-05-27 — просто календарный день) и локальное время (07:32:00). Это значит, что дата развёртывания остаётся настоящей датой, а не строкой, которую придётся разбирать заново.
Основную работу делают три элемента синтаксиса TOML. Заголовок [section] открывает таблицу; заголовок с точкой вроде [tool.ruff] вкладывает одну таблицу в другую без дополнительных отступов; а встроенная таблица { version = "1.0", features = ["derive"] } упаковывает небольшой объект в одну строку. Повторяющиеся секции используют удвоенный заголовок — массив таблиц:
[[servers]]
name = "alpha"
ip = "10.0.0.1"
[[servers]]
name = "beta"
ip = "10.0.0.2"
Этот блок превращается в JSON-массив из двух объектов под ключом servers. Он хорошо читается для плоского списка — а именно это и нужно большинству конфигураций инструментов.
Слабости реальны, но узки. В TOML нет типа null, поэтому отсутствующее значение — это просто опущенный ключ. Глубокая вложенность становится многословной, ведь каждый уровень повторяющейся вложенной структуры повторяет полный заголовок [[path]], и это шумнее, чем эквивалент на YAML. К тому же TOML моложе и менее универсален, чем JSON или YAML, поэтому его понимает не всякий инструмент. TOML хорош на конфигурации, которая в основном плоская, с горсткой помеченных секций, — а это описывает подавляющее большинство конфигураций инструментов.
Почему TOML для Rust и Python?
Взлёт TOML идёт по следам двух экосистем, принявших его как стандарт. Пакетный менеджер Rust, Cargo, использует Cargo.toml для каждого крейта, так что каждый Rust-разработчик читает и пишет TOML с первого дня. Python последовал за ним: PEP 518 ввёл pyproject.toml для объявления требований к сборке, а PEP 621 стандартизировал метаданные проекта (имя, версия, зависимости и секции [tool.*]) в том же файле, заменив россыпь из setup.py, setup.cfg и конфигов под каждый инструмент. Помимо этих двух, Hugo, Netlify, Poetry и Foundry по умолчанию используют TOML. Общая нить — это конфигурация инструментов, которую участники правят вручную и которой на пользу быть очевидной, а не хитрой.
Лицом к лицу — различия, которые кусаются
Таблицы возможностей аккуратны. Баги — нет. Вот конкретные различия, которые на практике ломают конфигурацию, и к каждому есть небольшой пример, который можно прогнать через любой парсер.
Комментарии
В JSON вообще нет синтаксиса комментариев. И TOML, и YAML используют # до конца строки.
# TOML: a leading comment
port = 5432 # and a trailing one
# YAML: identical comment style
port: 5432 # trailing comment
В тот момент, когда вы вставляете // или # в строгий JSON, разбор падает. Это не косметика. Конфигурация, которую человек сопровождает без комментариев, накапливает негласное знание, живущее только в чьей-то голове.
Типы данных и даты
Типы JSON явные, но скудные, без типа даты. Типы TOML явные и богатые. Типы YAML неявные и, как разобрано выше, порой ошибочные. Даты — самая явная граница. TOML выражает все четыре разновидности нативно:
odt = 1979-05-27T07:32:00Z # offset date-time (has timezone)
ldt = 1979-05-27T07:32:00 # local date-time (no timezone)
ld = 1979-05-27 # local date (a calendar day)
lt = 07:32:00 # local time
Сконвертируйте этот TOML в JSON — и каждое значение станет строкой, сохраняющей свой смысл: локальная дата останется "1979-05-27", а не раздуется в фальшивый timestamp с полуночью. Наш конвертер TOML в JSON сохраняет вид даты в точности — а многие наивные конвертеры делают это неправильно. YAML же может считать 1979-05-27 датой, а может и нет — в зависимости от парсера и версии схемы, и это именно та неоднозначность, которой в продакшн-конфигурации быть не должно.
Глубина вложенности
Чем глубже вложена конфигурация, тем сильнее расходятся форматы. Возьмём небольшое дерево в стиле Docker Compose:
services:
web:
build:
context: .
args:
NODE_ENV: production
ports:
- "8080:8080"
YAML выражает глубину чистыми отступами и остаётся компактным. Та же структура в TOML вынуждает прописывать полный путь в каждом заголовке и выносить скалярные значения вперёд, перед дочерними таблицами:
[services.web]
ports = ["8080:8080"]
[services.web.build]
context = "."
[services.web.build.args]
NODE_ENV = "production"
JSON несёт дерево во вложенных фигурных скобках — однозначно, но пунктуации много. На неглубокой конфигурации все три ощущаются похоже; после трёх-четырёх уровней YAML заметно стройнее, а TOML заметно более повторяющийся. Одно это различие объясняет, почему Kubernetes выбрал YAML, а Cargo выбрал TOML.
Чувствительность к отступам
О пробелах заботится только YAML. В JSON и TOML вы можете делать отступы как угодно или не делать вовсе — смысл не меняется. В YAML отступ и есть структура, а табы недопустимы, поэтому редактор, вставивший таб, или блок, вставленный не на той глубине, молча меняют документ или отказываются загружаться. Элемент списка, отступленный на один пробел дальше, чем нужно, может прицепиться не к тому родительскому ключу без единой ошибки — и это бесящий баг, который трудно поймать глазом, потому что файл всё ещё выглядит разумно. Если ваши участники используют разные редакторы и настройки, это постоянный налог, который YAML взимает, а другие два — нет; и это веский повод запускать линтер в CI на любой крупной конфигурации YAML.
Многострочные строки
JSON не может держать буквальный перевод строки внутри строки; вы экранируете его как \n. И у YAML, и у TOML есть настоящие многострочные строки.
{ "note": "line one\nline two" }
note: |
line one
line two
note = """
line one
line two
"""
| в YAML сохраняет переводы строк (а блок > вместо этого сворачивает их в пробелы), и """ в TOML ведёт себя похоже, отсекая перевод строки сразу после открывающих кавычек. Для встроенных скриптов, SQL или текста подсказки одного этого может хватить, чтобы выбрать формат.
Висячие запятые и строгость
JSON — самый строгий из трёх. Висячая запятая после последнего элемента массива — синтаксическая ошибка:
# valid TOML — the trailing comma is fine
ports = [
8001,
8002,
]
Та же висячая запятая после 8002 в JSON не разберётся, и поэтому добавить строку в JSON-массив так часто означает ещё и поправить запятую строкой выше. TOML разрешает висячую запятую, а списочный синтаксис YAML «тире на строку» полностью обходит этот вопрос. Строгость — достоинство для машинного обмена и помеха для ручного редактирования; это тот же компромисс, увиденный с другой стороны. Именно это трение JSON5 и JSONC и взялись убрать конкретно для JSON.
Большие целые числа и потолок 2^53
Все три формата умеют записать 64-битное целое как текст. Проблемы начинаются, когда это значение проходит через JavaScript, ведь числа JSON в браузере — это IEEE-754 double, а они могут точно представить целые лишь до 2^53 − 1 (9007199254740991). Snowflake ID или timestamp в наносекундах больше этого, поэтому проход туда и обратно через инструмент на JS округляет его и портит значение:
snowflake = 1420070400000000000 # exact in TOML text
{ "snowflake": "1420070400000000000" }
Надёжное решение в любом формате — хранить такие значения строками в кавычках, чтобы их вообще не касался числовой парсер. Это не изъян какого-то одного формата, а ограничение среды выполнения, которая делает конвертацию. Наши конвертеры предупреждают вас, когда целое пересекает эту черту, вместо того чтобы молча его испортить.
Как выбрать — матрица решения и блок-схема
Когда вы действительно решаете, какой формат конфигурации использовать, идите по вопросам по порядку и остановитесь на первом уверенном «да».
- Файл в основном пишут и читают программы, или экосистема уже предписывает формат? Если да — используйте JSON. Payload API — это JSON. Файл рядом с
package.json— это JSON. Не воюйте с инструментарием. - Люди ежедневно правят вручную глубоко вложенную конфигурацию внутри стека, который этого ожидает (Kubernetes, CI, Ansible)? Если да — используйте YAML и примите соглашение о кавычках, чтобы обойти проблему Норвегии.
- Это конфигурация инструмента или приложения — в основном плоская, с несколькими секциями, редактируемая участниками разного уровня, где типизированное и очевидное лучше краткого? Если да — используйте TOML.
Если два ответа сравнялись, разрешите ничью по этой таблице критериев:
| Требование | Лучший выбор | Почему |
|---|---|---|
| Комментарии в файле | TOML или YAML | В JSON их нет |
| Нативные значения даты/времени | TOML | Четыре явных типа даты |
| Очень глубокая вложенность | YAML | Отступы остаются компактными |
| Никаких сюрпризов с пробелами | JSON или TOML | Не чувствительны к отступам |
| Привязка к файлу экосистемы | JSON | package.json, tsconfig.json |
| Правки человеком, мало секций | TOML | Явные типы, заголовки в стиле INI |
| Обмен между машинами | JSON | Универсальный, строгий, быстрый |
| Вывод типов не должен угадывать | JSON или TOML | YAML выводит неявно |
Большинство проектов в итоге используют больше одного. В репозитории может быть JSON package.json, который никогда не правят руками, TOML pyproject.toml для Python-инструментария, YAML-воркфлоу в .github и файл .env для локальных секретов, который не следует ни одной из этих грамматик вовсе. Такая смесь — это норма, и в этом весь смысл: каждый файл живёт в формате, подходящем его редактору. Если слой .env — часть вашей настройки, наше руководство по формату файла .env рассказывает, где он вписывается рядом с JSON-конфигурацией и как конвертировать между ними.
Конвертация между TOML, JSON и YAML
Конвертировать между этими форматами приходится чаще, чем можно ожидать. CI-задача читает написанный вручную pyproject.toml и хочет получить его в JSON, чтобы скормить панели зависимостей. Миграция переводит приложение с конфигурации YAML на TOML ради более строгой типизации. Ревью кода проще, когда вы сравниваете JSON-форму двух файлов, а не их чувствительные к пробелам оригиналы. У каждого направления есть подводный камень, который стоит знать до того, как вставлять.
TOML в JSON. Главный риск — даты. Локальная дата вроде 1979-05-27 должна остаться календарной датой, а не превратиться в полуночный timestamp в UTC, выдумывающий время и часовой пояс. Комментарии тоже теряются, ведь JSON не может их хранить. Конвертер TOML в JSON точно сохраняет каждый вид даты, так что преобразования туда и обратно остаются без потерь.
JSON в TOML. TOML строже к структуре, поэтому конвертацию могут заблокировать две вещи. Верхний уровень должен быть объектом, ведь документ TOML в корне всегда таблица; голому массиву или скаляру там некуда деться. И в TOML нет null, поэтому null-значение объекта отбрасывается (с предупреждением, перечисляющим, какие ключи), а null внутри массива вообще не имеет допустимого представления в TOML. Конвертер JSON в TOML явно показывает оба случая вместо того, чтобы выдавать сломанный результат.
JSON и YAML, в обе стороны. Здесь надо следить за проблемой Норвегии: NO или 0755 без кавычек могут сменить тип при пересечении границы. Конвертер JSON в YAML и конвертер YAML в JSON берут кавычки на себя, чтобы строка осталась строкой.
После любой конвертации результат стоит проверить. Прогон вывода через форматировщик JSON подтверждает, что JSON корректен и единообразно отформатирован, прежде чем вы закоммитите его или отправите дальше. И поскольку файлы конфигурации несут секреты (registry-токены, учётные данные баз данных, деплой-ключи), все эти инструменты работают целиком в вашем браузере: ничего не загружается на сервер, так что Cargo.toml с приватным registry-токеном или values-файл с учётными данными никогда не покидает вашу машину.
FAQ
TOML лучше YAML?
Универсально лучше нет ни того, ни другого; выбор TOML vs YAML сводится к форме данных. В TOML труднее ошибиться благодаря явным типам и отсутствию ловушек с отступами, поэтому он выигрывает для в основном плоской конфигурации инструментов. YAML компактнее выражает глубокую вложенность, поэтому выигрывает для крупной слоистой инфраструктуры вроде Kubernetes.
JSON или YAML для файлов конфигурации?
Для конфигурации, которую редактирует человек, предпочтите YAML: он допускает комментарии и чисто читается, чего нет у JSON. Для конфигурации, которую программы создают и потребляют, предпочтите JSON: он строгий, быстрый и универсальный. Разделительная линия — кто редактирует: человек или машина.
Почему JSON не допускает комментарии?
Создатель JSON, Дуглас Крокфорд, убрал комментарии намеренно. Он опасался, что люди будут встраивать в них директивы разбора и ломать совместимость между парсерами. Если вам нужны комментарии в файле JSON-образной формы, используйте JSON5 или JSONC — они возвращают их, не меняя базовую структуру.
Что такое проблема Норвегии в YAML?
Это когда YAML 1.1 трактует некоторые слова без кавычек как булевы значения, так что country: NO становится false вместо строки "NO". YES, ON и OFF ведут себя так же. Кавычки вокруг значения это предотвращают; полную историю смотрите в отдельном руководстве по ссылке выше.
Поддерживает ли TOML комментарии?
Да. TOML использует # для комментариев — как на отдельной строке, так и в конце значения, ровно как YAML. Учтите, что комментарии теряются при конвертации TOML в JSON, ведь у JSON нет синтаксиса комментариев, чтобы их хранить.
Может ли TOML представить всё, что умеет JSON?
Почти. Разрыв JSON vs TOML маленький, но реальный: в TOML нет типа null, поэтому null из JSON при конвертации отбрасываются или отклоняются, а документ TOML на верхнем уровне обязан быть таблицей, так что голый JSON-массив или скаляр не сконвертировать, пока их сначала не завернуть под ключ.
YAML — это надмножество JSON?
Начиная с YAML 1.2 — да: любой валидный документ JSON одновременно валиден и как YAML, так что парсер YAML может читать JSON напрямую. Это полезный факт, но не полагайтесь на него как на границу безопасности, ведь парсеры YAML добавляют возможности (например, якоря), которых у JSON нет.
Какой формат конфигурации разбирается быстрее всего?
JSON обычно самый быстрый — с самыми зрелыми и оптимизированными парсерами в каждом языке. YAML разбирается медленнее и сложнее всех из-за своей большой спецификации и неявной типизации. TOML посередине — по скорости ближе к JSON, чем к YAML.