Уникальная нотация языка UNO создаёт ему огромный потенциал, позволяющий в перспективе заменить большинство нынешних языков разметки данных.
Понятно, что XML и JSON имеют собственные ниши, в которых они могут находиться вечно, но существует огромный ряд задач, где даже их можно с выгодой заместить. Что же касается таких специализированных форматов, как TOML, YAML, KDL, HCL или EDN, то при повсеместном внедрении UNO они рискуют потерять свою актуальность. Из-за врожденных синтаксических недостатков, медленного парсинга или скрытых ошибок, обусловленных недостаточным проектированием, эти языки могут остаться лишь в легаси-проектах. Верно ли данное утверждение? Давайте разбираться.

Если вы ещё не знакомы с UNO, ознакомьтесь с первой частью публикации или со спецификацией языка разметки.
Немного истории
«Три кольца — пресветлым эльфам под высоким небом,
Семь — владыкам гномов в каменных пещерах,
Девять — людям смертным, чей удел — могила...»
Первый распространённый формат появился в 1978 году — это был CSV. INI примерно в 1985 году. А XML только в 1998. И через три года, в ответ на него, появился JSON и YAML. Каждый из них имел свои принципиальные особенности и предназначение.
Спустя десяток лет возникла целая плеяда интерпретаций JSON: HOCON, JSON5, EDN. Следом возникали TOML, Apache Parquet, UCL, Hjson, Jsonnet, JSON Lines, ELDF, HCL, RON. Спустя пару десятилетий после JSON-а в 2021 году появились CML и KDL.
Современная ИТ-индустрия попыталась раздать каждому сословию по своему «кольцу»:
JSON — отдали веб-разработчикам для быстрых API.
XML — сослали в суровые пещеры банковского Enterprise-сектора.
YAML и TOML и другие — вручили DevOps-инженерам для вечной борьбы с конфигурациями серверов.
В итоге мы получили разрозненный мир, где каждый заперся в своей нише, а программистам приходится постоянно изучать и приручать этих синтаксических монстров. Но что, если существует та самая единая нотация, способная объединить их лучшие свойства?
«Одно Кольцо, чтоб править всеми, Одно Кольцо, чтоб все найти...»
Предлагаю начать комплексный анализ возможностей UNO по критериям, представленным в данной таблице:

Читаемость
«Ибо в воде есть некая первозданная чистота, не тронутая искажением. Она течёт свободно, ясная для взора, и нет в ней ничего лишнего...»
Базовый синтаксис мы рассмотрели в первой части и уже тогда убедились в его исключительной наглядности. Проведем дополнительные сравнения. В качестве примера рассмотрим описание книги.
Про XML объяснять ничего не требуется, всем известна его многословность (аж глаз задёргался при взгляде на пример):
<?xml version="1.0" encoding="UTF-8"?> <книга rating="4.9"> <артикул>LOTR-SILM-01</артикул> <название>Сильмариллион</название> <авторы> <автор>Дж. Р. Р. Толкин</автор> <автор>Кристофер Толкин</автор> </авторы> <основной_жанр>Эпическое фэнтези</основной_жанр> <аннотация> История Первой Эпохи Средиземья, создания Сильмарилей и падения Моргота. Древняя хроника, определившая лицо современной мифологии. </аннотация> <!-- Коммерческая информация --> <цена>1200</цена> <валюта>rub</валюта> <тираж тип_данных="u32">50000</тираж> <вес>650</вес> <единица_измерения>g</единица_измерения> </книга>
Кавычки и скобки JSON сильно отвлекают от содержимого, а комментарии не являются нативными — ради них приходится городить фейковые строковые ключи:
{ "книга": { "rating": 4.9, "артикул": "LOTR-SILM-01", "название": "Сильмариллион", "авторы": [ "Дж. Р. Р. Толкин", "Кристофер Толкин" ], "основной жанр": "Эпическое фэнтези", "аннотация": "История Первой Эпохи Средиземья, создания Сильмарилей и падения Моргота.\nДревняя хроника, определившая лицо современной мифологии.", "//": "Коммерческая информация", "цена": 1200, "валюта": "rub", "тираж": 50000, "вес": 650, "единица_измерения": "g" } }
В TOML всё выглядит в целом неплохо. Однако немного путают квадратные скобки, имеющие двойное назначение (объявление секций и массивов), ну и от кавычек мы здесь избавлены далеко не полностью:
[книга] rating = 4.9 артикул = "LOTR-SILM-01" название = "Сильмариллион" авторы = [ "Дж. Р. Р. Толкин", "Кристофер Толкин" ] # Кавычки обязательны из-за пробела в ключе "основной жанр" = "Эпическое фэнтези" аннотация = """ История Первой Эпохи Средиземья, создания Сильмарилей и падения Моргота. Древняя хроника, определившая лицо современной мифологии.""" # Вложенная коммерческая информация [книга.коммерция] цена = 1200 валюта = "rub" тираж = 50000 # Увы, тип u32 потерян — для TOML это обычный i64 вес = 650 единица_измерения = "g"
А вот UNO — это чистый изумруд. В нём меньше всего визуальных маркеров разметки, наглядная и удобная структура (оцените хотя бы удобство копирования многострочного текста без дополнительных отступов):
книга: # рейтинг= 4.9 артикул: LOTR-SILM-01 название: Сильмариллион авторы: - Дж. Р. Р. Толкин - Кристофер Толкин основной жанр: Эпическое фэнтези аннотация: ' История Первой Эпохи Средиземья, создания Сильмарилей и падения Моргота. Древняя хроника, определившая лицо современной мифологии. ' // Коммерческая информация цена= 1200 rub тираж= u32:50000 вес= 650 g
Кроме того, обратите внимание на цену и вес, которые сразу содержат не только величину, но и единицу измерения. В тираже мы чётко указали тип данных. И посмотрите, как органично встали метаданные (`# рейтинг= 4.9`), не разрывая восприятие содержимого.
Типизация
«Всякому же из них Илуватар указал свою долю и наделил каждого особым разумением... Дабы ведали они, что вода подвластна Улмо, а ветры и воздух — Манвэ, земная же твердь — Ауле... Ибо каждый из них верен своей сути и не может соделаться иным, как и сама Музыка не терпит фальши»
В популярных языках разметки с типизацией ситуация не очень хорошая. В XML абсолютно всё является строкой (машине приходится гадать на XSD-схемах). В JSON и YAML типы угадываются неявно, из-за чего строка 1.10 превращается в число 1.1, а кодовое имя Норвегии NO парсеры считывают как логическое false. Тип числа угадывается парсером, что тоже не добавляет ему скорости и точности. В YAML есть механизм приведения типов (не_логика: !!str NO), но без контроля разрядности. В KDL есть аннотации типов (тираж (u32)50000) Достаточно проработана типизация в EDN благодаря механизму нативных тегов (#uuid "..."), однако его синтаксис намертво привязан к собственной экосистеме.
В UNO типизация возведена в абсолют. Здесь тип данных определяется тремя способами в зависимости от требований:
1. Синтаксическая типизация (благодаря дуализму разделителей записи):
строка_текста: Произвольный текст без кавычек строгое_число= 42 логический_флаг= true
По умолчанию все числа Float64, но если вас это не устраивает, вы можете задать требуемый тип.
2. Явная префиксная типизация:
Целое со знаком= i32:50000 Целое = u8:149 Шестнадцатеричное= 0x:1a45f0d16 Пользовательский тип= any_type:45_12
3. Указание типа в проверочном шаблоне (рассмотрим далее).
Метаданные
«И в чертогах Мандоса, где вершатся судьбы, всё явленное в мире имеет своё незримое отражение. Каждому созданию Илуватара там предначертан свой тайный удел, сокрытый от взоров живущих, но ведомый Хранителю. И печать эта следует за ними повсюду, определяя их суть в общем замысле...»
В традиционных форматах концепция метаданных развита крайне слабо. В JSON, YAML и TOML их просто нет — разработчикам приходится загрязнять структуру фейковыми ключами вида _id или __metadata. В EDN метаданные прикрепляются через символ ^, но они нависают сверху над объектом, что немного неудобно.
Королём метаданных ранее заслуженно считался XML. Однако, как говорится, да здравствует новый король!
Давайте сравним простой пример разметки на XML (HTML) и UNO:
<article class="book" genre="fantasy" id="f123"> <h1>Сильмариллион</h1> <p>Был Эру, Единый, кого в Арде зовут Илу́ватар.</p> </article>
article: # class: book # genre: fantasy # id: f123 h1: Сильмариллион p: Был Эру, Единый, кого в Арде зовут Илу́ватар.
В чём фундаментальная разница:
В XML метаданные (атрибуты) зашиты прямо внутрь открывающего тега. Считывать их крайне неудобно.
В UNO вложенные метаданные расположились непосредственно под целевым свойством с отступом, образуя с ним как бы единый объект. Это позволяет человеку мгновенно сканировать записи, полностью игнорируя служебную информацию, если она в данный момент не нужна, либо как раз сосредоточиться на ней. При этом однопроходный парсер на Rust точно знает, к какому узлу привязать эти свойства.
Сборка данных
«Валар взяли последний золотой плод Лаурелин и последний серебряный цветок Телпериона, уцелевшие в час великой тьмы. Очистив их от скверны, они поместили их в священные ладьи-сосуды, дабы несли они первозданный свет по небесному своду, сменяя друг друга и наполняя Арду гармонией...»
В традиционном IT-мире управление большими конфигурациями — это вечная головная боль. JSON принципиально не умеет собираться из нескольких частей. YAML и TOML для разделения на модули требуют внешних скриптов сборки, что далеко не идеально.
Яркими исключениями на этом фоне выглядят HOCON со своей директивой include и HCL, умеющий автоматически собирать в единый граф все файлы в директории. Но и здесь не всё гладко:
В HOCON работает скрытое автослияние. Если вы подключаете внешний файл, и там обнаруживается ключ с таким же именем, как в основном документе, HOCON без предупреждений склеит их свойства в один объект. В огромных проектах это часто приводит к «тихим багам»: вы думали, что просто импортируете константу, а HOCON за вашей спиной неявно модифицировал структуру данных.
В HCL файлы внутри одной папки намертво делят общую область видимости, лишая разработчика возможности явно контролировать изолированность и порядок импорта.
В UNO Multifiles предусмотрено два принципиально разных механизма сборки данных из внешних файлов:
@import— выполняет полное добавление записей из внешнего файла, внедряя их непосредственно в текущий документ.@use— инструмент изолированного, точечного заимствования записей по мере необходимости через псевдонимы.
Выглядит это следующим образом:
// Объявляем псевдоним для внешнего файла @use out: file.uno // В точечной нотации забираем значение нужного ключа с помощью модификатора заимствования key_direct:& out.key-x
В UNO любые манипуляции с данными — это всегда явный, контролируемый автором контракт. Никакой скрытой магии за спиной разработчика. Если вы хотите дополнить объект из импорта, вы обязаны явно использовать модификатор +, а если хотите забрать константу — применить маркер заимствования & . Если же имена ключей в файлах просто совпадут, однопроходный парсер на Rust не станет гадать и склеивать их, а предсказуемо применит базовое правило линейной перезаписи данных.
Модификация данных
«И кузнецы Ногрода взяли черный меч Англахель, чьё лезвие таило в себе силу упавшей звезды. Они перековали его заново: очистили сталь от зазубрин, перебалансировали рукоять и вернули былую прочность, но наделили клинок новым бледным пламенем, что горело на самой кромке лезвия...»
В традиционных форматах данные статичны. Если вам нужно изменить конфигурацию под конкретную среду или дополнить объект, вы вынуждены либо переписывать его заново, либо плодить тонны дубликатов. Некоторые форматы поддерживают модификацию данных.
YAML умеет переиспользовать куски данных с помощью механизмов анкоров и мержей, но использует знаки & и * в значениях с точностью до наоборот. Умеет объединять только объекты.
HOCON — чертовски мощный формат, созданный для конфигураций сложных Java-систем. Он умеет делать интерполяцию и слияние объектов по умолчанию. Поддерживает только перезапись или слияние в плюс. Алгоритм разрешения подстановок ${} в HOCON требует многопроходного парсинга, из-за чего он довольно медленный и сложный в реализации.
HCL (язык Terraform) — это, строго говоря, уже не просто формат разметки, а полноценный декларативный язык программирования. Чтобы делать модификации в HCL, вам нужно отдельно объявлять блоки variable { ... }, отдельно — блоки locals { ... }. Файл разметки превращается в тяжеловесный программный код, который обычный человек или аналитик без навыков программирования написать уже не сможет.
В UNO Advanced модификации превращают парсинг в изящный, интуитивно понятный, хотя тоже требующий вдумчивости процесс.
Для лучшего понимания как всё работает приведу полную схему выражения UNO.
keypart valuepart ╭─────────────┴───────────╮ ╭─────┴──────╮ | Indent | DIRECTIVE | Imark | KEY, Delimiter, Modifier | Sep | VALUE | ╰────────────────────────┬───────────────────────╯ record
Директива, как вы уже знаете, обозначается знаками # — для метаданных, @ — для сборки, а для валидации (рассмотрим далее) — $.
Делители — это двоеточие или равно.
Модификатор — символ, указывающий на команду обработки данных. Ставится сразу после разделителя имени ключа без пробела.
Модификаторы позволяют:
*— отметить запись-донор (запомнить для последующего использования)&— получить значение указанного ключа целиком+— дополнить значение ключа указанным-— вычесть из значения ключа указанное>— указать логическую связь
Тема достаточно обширная, ознакомиться с нею можно на сайте спецификации , приведу лишь только несколько примеров.
Заимствовать можно не только значение ключа целиком (как в примере предыдущей главы), но и использовать его для встраивания в строку:
Дом эльфов:* Финвэ Имя сына:* Нолдоран // … // Сын великого короля Нолдор наследует имя дома Полное имя сына короля:& '{Имя сына} {Дом эльфов}лоло' // Нолдоран Финвэлоло
Пример слияния списков:
кольца_власти:* - Три — эльфийским владыкам - Семь — владыкам гномов // … Спустя время Саурон куёт новые кольца, и мы дополняем список: кольца_власти:+ - Девять — людям смертным - Одно — Владыке Тьмы
В старых статических форматах (JSON или YAML) повторное объявление ключа кольца_власти привело бы к тому, что новые строки просто намертво затёрли бы в памяти старые. В UNO оператор + соберёт в памяти единую монолитную структуру:
кольца_власти: - Три — эльфийским владыкам - Семь — владыкам гномов - Девять — людям смертным - Одно — Владыке Тьмы
Таким образом, UNO позволяет самыми разными способами производить слияние и вычитание списков, объектов, текста, чисел или логических выражений. Получать, дополнять или вычитать значения донорских ключей. И самое главное: вся эта сложная динамика разворачивается прямо в оперативной памяти компьютера за один единственный проход парсера на Rust.
Валидация
«Ибо Илуватар послал в сердце мира Огонь Неугасимый, и мир обрёл бытие; и всякий, кто пытался внести в него искажение или фальшь, разбивался о нерушимость этого великого Закона...»
Как часто кривые или неполные конфигурационные файлы роняли ваш бэкенд в продакшене просто потому, что парсер молча пропустил не тот тип данных или опечатку в ключе? В старом мире разметки валидация — это всегда внешняя надстройка. Для JSON приходится подключать громоздкие библиотеки JSON Schema, для XML — писать километровые XSD-файлы. Сам парсер об этих проверках ничего не знает, тратя ресурсы на двойную обработку текста.
В экосистеме UNO за безопасность отвечает встроенный модуль UNO Verify (Шаблоны и Схемы). Это тот самый Огонь Неугасимый для вашей разметки. Любое синтаксическое искажение, неверная разрядность числа или пропущенное обязательное поле будут обработаны компилятором на Rust ещё на этапе парсинга.
За проверку синтаксиса отвечает UNO Shema, но она нужна только для разработчиков парсеров. Для прикладных задач достаточно UNO Template — это такой шаблон ваших данных с проверочной метаинформацией. Давайте сразу проведём сравнение с XSD Shema:
Чтобы окончательно понять разницу в когнитивной нагрузке, давайте сравним, как выглядит ограничение веса артефакта (дробное число от 5.5 до 150.0 граммов) в классическом XSD и в UNO Template.
К сожалению, по стандарту W3C в XSD встроены всего два примитивных IEEE-типа для чисел с плавающей точкой:
<xs:element name="вес_кольца"> <xs:simpleType> <xs:restriction base="xs:float"> <xs:minInclusive value="5.5"/> <xs:maxInclusive value="150.0"/> </xs:restriction> </xs:simpleType> </xs:element>
В UNO всё выглядит на порядок лаконичнее и изящнее. Но самое главное: проверка данных не запускается отдельным тяжеловесным процессом после парсинга. Она происходит прямо в процессе анализа файлов однопроходным движком на Rust и мы получаем именно тот тип, который нам необходим:
вес_кольца= $ type: f8 $ min= 5.5 $ max= 150.0
Снова шах и мат! Аплодисменты.
Эпилог
«...и хоры айнуров и людей породят музыку более великую. Тогда замысел Илуватара будет наконец воплощён в полной мере, ибо каждый узнает свою цель и каждый будет понимать другого, сливаясь в единой гармонии бытия...»
Способен ли UNO заменить TOML, YAML, KDL, HCL и EDN?
Да, способен. Потому что он убирает удушающий визуальный шум кавычек и скобок, полностью исключает неявное угадывание типов данных, защищает данные валидацией и превращает статичную текстовую разметку в живую, динамическую систему.
Но наличие потенциала ещё не обуславливает непреклонное свершение. Предстоит сделать очень многое, а главное — найти единомышленников. Людей, способных критически относиться к привычному легаси, понимая, что наш ИТ-мир далеко не идеален. Людей, готовых на мгновение приостановить бег современной жизни, осознать происходящее и поддержать проект хотя бы словом или ценным архитектурным советом.
Несмотря на глубокую проработку концепции Универсальной Нотации Объектов, остаётся ещё ряд открытых вопросов. Мне важно спроектировать абсолютно монолитную и сбалансированную систему, которая станет принципиально более удобной и надёжной в реальной эксплуатации. Именно поэтому я не спешу, а терпеливо шлифую грани изумруда, который однажды будет инкрустирован в нерушимый сплав системного кода.
Давайте обсудим
Что вы думаете по поводу общей структуры выражений UNO, где гармонично соседствуют директивы и модификаторы? Как вам концепция отражения метаданных и общего единства директив?А гибкая система прямого импорта данных и изоляция пространств имён? Понравился ли синтаксис валидации в UNO Template? Для каких практических задач в вашей работе могла бы потребоваться динамическая модификация данных и каких новых возможностей вам хотелось бы ещё?
Изучить полную спецификацию и следить за развитием проекта можно на сайте https://uno.mudrium.ru/. Чуть позже появится и его англоязычная версия.
Делитесь своей конструктивной критикой и мыслями в комментариях. Вы способны напрямую повлиять на новую технологию, которую, возможно, начнёте использовать через несколько лет. Согласитесь, ведь чертовски приятно пользоваться инструментом, который ты самолично дошлифовал до идеала, и чувствовать искреннюю гордость от своего вклада в полезное дело!

