Конвертер JSON в YAML
Переводит JSON в YAML и объясняет каждые добавленные кавычки — строки, которые без них молча стали бы логическим значением, числом или датой.
name: deploy
country: 'NO'
startsAt: '12:30'
mode: '0755'
version: '1.10'
released: '2024-01-30'
enabled: 'yes'
script: |-
set -e
npm run build
npm test
replicas: 3
tags:
- web
- edge
Значений в кавычках: 6. Нужны только в YAML 1.1: 4. Переключите схему выше, чтобы увидеть разницу.
countryтолько 1.1«NO» прочиталось бы как логическое false.
startsAtтолько 1.1«12:30» прочиталось бы по основанию 60 как 750.
modeУ «0755» ведущий ноль, поэтому оно прочиталось бы как число 493.
version«1.10» прочиталось бы как число 1.1.
releasedтолько 1.1«2024-01-30» прочиталось бы как дата, а не как текст.
enabledтолько 1.1«yes» прочиталось бы как логическое true.
Что делает этот инструмент
Он переводит документ JSON в YAML, а затем говорит, почему каждая строка оказалась в кавычках. Эта вторая часть и есть причина существования инструмента: любой другой конвертер отдаёт результат и оставляет вам обнаружить позже, что одно из ваших значений больше не строка.
Преобразование идёт в одну сторону. Читать YAML — задача куда большая, чем писать его, а отчасти верный разборщик YAML хуже, чем никакой: он принимает ваш файл и без жалоб возвращает неверные данные. Вывод — задача ограниченная, и именно это направление здесь покрыто.
Почему конвертеру вообще нужны мнения
JSON говорит, какого типа всё. У строки есть кавычки, у числа нет, и третьей возможности не существует. YAML же определяет тип некавыченного значения по его виду: совпало с образцом логического — значит логическое, совпало с числом — значит число, и лишь если не совпало ни с чем, остаётся текстом.
Именно это делает YAML приятным для письма от руки и превращает преобразование в вопрос суждения. Каждую строку на входе приходится сверять с каждым образцом, который YAML разрешает, и заключать в кавычки при совпадении. Ошибётесь в осторожную сторону — вывод шумный; ошибётесь в другую — значение молча меняет тип.
Норвежская проблема и её родня
Самый известный случай — список кодов стран. Норвегия — это NO, а в YAML 1.1 некавыченный токен NO есть логическое false. Файл настроек, перечисляющий страны, теряет Норвегию и приобретает false, и нигде ничто не сообщает об ошибке.
Это не одно странное правило, а целое семейство. YAML 1.1 читает y, Y, yes, no, on и off как логические значения в любом написании, и тем ловит химический символ, положение переключателя и ответ на вопрос. А числовые разрешители ещё чуднее:
- 12:30 — это 750. YAML 1.1 читает разделённые двоеточиями цифры по основанию 60, так что время суток или длительность становятся целым числом.
- 0755 — это 493. Ведущий ноль означает восьмеричное в YAML 1.1, а в YAML 1.2 тот же текст — десятичное 755: версии расходятся в том, какое число, а не в том, число ли это.
- 1.10 — это 1.1. Двухчастный номер версии есть число с плавающей точкой, и завершающий ноль исчезает. Зависимость, прикреплённая к 1.10, теперь указывает на 1.1.
- 2024-01-30 — объект даты, а не строка, потому что у YAML 1.1 есть тип метки времени.
- Пустая строка — это null, как и голые слова null, Null, NULL и тильда.
Ничто из этого не ошибка YAML. Это разрешители делают ровно то, что обещают, над текстом, который случайно совпал. Единственная защита — заключать в кавычки всё совпадающее, что этот инструмент и делает, а панель находок отчитывается за это построчно.
Две версии, и почему по умолчанию старшая
YAML 1.2 вышел в 2009 году и убрал большинство неожиданных разрешителей. Его базовая схема оставляет логическими только true и false, полностью отказывается от основания 60 и не имеет типа метки времени. Под 1.2 NO, 12:30 и 2024-01-30 — просто строки.
Загвоздка в том, что именно читает ваш файл. PyYAML реализует YAML 1.1, а PyYAML стоит за огромным количеством инструментов — Ansible, старые клиенты Kubernetes, бесчисленные сценарии. Go’шный yaml.v3 и нынешний js-yaml следуют 1.2. Так что один и тот же документ читается двумя разными способами в зависимости от того, кто его открыл, и единственный безопасный везде вывод — закавыченный под 1.1.
Это и есть здешнее умолчание. Переключение на 1.2 ничего не прячет: вывод строится заново с новыми разрешителями, а панель находок сокращается, так что видно в точности, какие кавычки стояли ради старой схемы. Оставшиеся нужны любому разборщику.
Строки, ломающие синтаксис, а не тип
Вторую группу строк приходится закавычивать по другой причине: не потому что YAML прочёл бы их как иной тип, а потому что они вовсе не разобрались бы как текст.
- Двоеточие с последующим пробелом заканчивает ключ. «note: time: now» прочиталось бы как ключ note, значением которого является ключ time.
- Пробел с последующей решёткой открывает комментарий, и всё, что за ним, исчезает.
- Начальные -, ?, :, [, ], {, }, #, &, *, !, |, >, %, @ или обратный апостроф — символы-индикаторы и означают нечто структурное.
- Ведущий или замыкающий пробел некавыченное значение не сохраняет, поэтому « x » возвращается как «x».
- Табуляция где угодно внутри значения отвергается начисто: PyYAML отказывает всему документу вместо того, чтобы прочесть его неверно, так что этот случай падает громко.
Одинарные кавычки используются везде, где их хватает: у них ровно одно правило экранирования — апостроф пишется дважды — и читаются они лучше, чем обратно-слэшевые экранирования, которые приносят двойные. Двойные оставлены тому, что действительно требует экранирования: управляющим символам, табуляциям и многострочным строкам, которым блок недоступен.
Многострочные строки и указатель обрезки
Строка с переводами строки — сценарий, сертификат, кусок прозы — обычно и есть причина, по которой YAML вообще понадобился. JSON способен записать её лишь экранированиями обратный-слэш-n в одной очень длинной строке; у YAML есть литеральный блочный скаляр, открываемый вертикальной чертой, под которой текст стоит с отступом и читаемо.
Тонкость в том, что происходит с завершающими переводами строки, и этим управляет указатель обрезки:
- Голая вертикальная черта подрезает: сколько бы завершающих переводов у блока ни было, значение получает ровно один.
- Вертикальная черта с минусом срезает: значение не получает ни одного.
- Вертикальная черта с плюсом сохраняет: значение получает их все.
Этот инструмент выбирает указатель по той строке, которую получил, так что значение переживает путь туда и обратно в точности. Знать об этом стоит потому, что умолчание — голая черта — это то, что пишут от руки, и оно молча нормализует строку, оканчивавшуюся двумя переводами или ни одним.
Блочный скаляр вмещает не всё, и там, где не вмещает, вывод откатывается к двойным кавычкам, а панель находок объясняет почему. Возврат каретки не выживает, потому что блочные скаляры нормализуют переводы строк. Первая строка, начинающаяся с пробела, была бы прочитана как лишний отступ и снята. А строка, оканчивающаяся пробелом, спецификацией сохраняется, но на экране невидима и большинством редакторов удаляется при сохранении, так что кавычки надёжнее — это решение, а не ограничение, и о нём сообщается именно так.
Больше одного документа
У YAML есть то, чего нет у JSON: файл может содержать поток документов, разделённых тремя дефисами. Это формат манифеста Kubernetes, и потому массив JSON так часто должен стать чем-то иным, нежели последовательность YAML.
Переключатель здесь выводит каждый элемент массива верхнего уровня отдельным документом. А когда ввод вовсе не одно значение JSON, но каждая строка разбирается сама по себе, он читается как NDJSON — построчный формат, в котором приходят журналы и выгрузки API, — и каждая строка становится документом. Такое прочтение есть догадка, поэтому о нём сообщается над выводом, а не делается молча.
Откат применяется лишь когда весь ввод не разбирается, а каждая строка разбирается, чего просто испорченный документ не сделает. Синтаксическая ошибка по-прежнему всплывает как синтаксическая ошибка, со строкой и столбцом, где произошла.
Чего преобразование сохранить не может
Две вещи теряются ещё до того, как инструмент увидит ваши данные, обе в самом разборе JSON, и стоит знать какие.
Повторяющиеся ключи. JSON позволяет объекту дважды перечислить один ключ, и большинство разборщиков молча оставляет последний. YAML запрещает дубликаты вовсе, так что вывод окажется корректным, но прежнее значение уже потеряно — и никакой конвертер не сообщит о том, чего никогда не получал.
Точность целых. Число JSON больше примерно девяти квадриллионов не переживает разбор в double, поэтому идентификатор вроде 12345678901234567890 возвращается округлённым. Это не беда YAML и не беда, вносимая этим инструментом; так происходит в любом разборщике JSON в языке. Если большой идентификатор важен, его место в строке по обе стороны.
Замечания о выводе
Отступ делается пробелами, всегда, потому что YAML запрещает табуляции для отступа полностью — это одна из немногих вещей, в которых формат строг. Два пробела — общепринято; четыре предлагаются потому, что некоторые внутренние стили их требуют.
Последовательности идут с отступом под своим ключом. И эта форма, и форма без отступа — корректный YAML и значат одно и то же; отступную пишет большинство людей, и её же правильно сворачивает большинство редакторов.
Пустой массив или объект записывается потоковым стилем парой скобок, потому что блочный стиль не имеет способа выразить пустоту: на следующих строках писать нечего.
Вывод завершается переводом строки, и это несёт функцию, а не аккуратность. Обрезка блочного скаляра отмеряется от следующего за ним перевода строки, поэтому подрезанный блок в самом конце файла без завершающего перевода теряет тот перевод, который должен был сохранить.
Частые вопросы
- Является ли JSON уже корректным YAML?
- Под YAML 1.2 — да: спецификация говорит об этом прямо, и разборщик 1.2 прочтёт файл JSON напрямую. На практике пользы немного, потому что причина преобразовывать — читаемость: комментарии, блочные скаляры, отсутствие фигурных скобок, — а вставка JSON в файл YAML не даёт ничего из этого. Под YAML 1.1 это не вполне верно, что ещё одна причина различать версии.
- Почему моей строке достались кавычки, которые ей будто бы не нужны?
- Почти наверняка нужны. YAML типизирует некавыченное значение сопоставлением образцов, так что NO, yes, off, 12:30, 0755, 1.10, 2024-01-30 и пустая строка — все перестают быть строками. Панель находок называет каждый случай и показывает значение, которым он стал бы, чтобы утверждение можно было проверить, а не принять на веру. Если вы целитесь в разборщик YAML 1.2, смена схемы уберёт те, что нужны только 1.1.
- В чём здесь разница между YAML 1.1 и 1.2?
- 1.2 отказался от разрешителей, вызывающих большинство неожиданностей: yes/no/on/off больше не логические, основание 60 исчезло, а типа метки времени нет. PyYAML реализует 1.1 и по-прежнему повсюду, поэтому осторожный вывод стоит по умолчанию; настройка 1.2 нужна тогда, когда вы знаете, что будет читать файл.
- Может ли он перевести YAML обратно в JSON?
- Нет, намеренно. Читателю YAML нужны якоря, псевдонимы, теги, ключи слияния, пять стилей скаляров и две версии схемы, а тонко ошибиться в любом из них — значит принять файл и вернуть данные, отличные от тех, что в нём были. Такой отказ беззвучен, и это делает его хуже, чем отсутствие возможности вовсе.
- Как получить многодокументный файл в духе Kubernetes?
- Включите переключатель, который выводит каждый элемент массива верхнего уровня отдельным документом, и массив станет документами, разделёнными тремя дефисами. Если же ваш ввод — NDJSON, по одному объекту JSON в строке, как часто приходят журналы и выгрузки API, это распознаётся само и сообщается над выводом.
- Почему в выводе нет комментариев?
- Потому что их не было во вводе. Комментарии — главное, что есть у YAML и нет у JSON, а конвертер не может их выдумать. Это стоит помнить и в обратную сторону: если прогнать файл YAML через JSON и назад, всякий комментарий в нём исчезнет.
- Отправляется ли что-нибудь из вставленного на сервер?
- Нет. Разбор и преобразование целиком выполняются в вашем браузере; ничто не загружается и не записывается, и всё работает без сети.