Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Комментарии в формате JSON

Наткнулся случайно на ссылку, где автора очень изящно решает проблему комментариев в формате «джейсон». Это лучшее решение, которое я только видел (обычно либо выделяют специальный ключ, либо расширяют формат). Только поглядите:

{
  "api_host" : "The hostname of your API server. You may also specify the port.",
  "api_host" : "hodorhodor.com",

  "retry_interval" : "The interval in seconds between retrying failed API calls",
  "retry_interval" : 10,

  "auth_token" : "The authentication token. It is available in your developer dashboard under 'Settings'",
  "auth_token" : "5ad0eb93697215bc0d48a7b69aa6fb8b",

  "favorite_numbers": "An array containing my all-time favorite numbers",
  "favorite_numbers": [19, 13, 53]
}

Он полностью синтаксически верен и разбирается правильным образом во всех языках, по всей видимости.

37 комментариев
alxt 2014

Работает. Но что-то в этом кривое. Хотя для web’а это привычно- там всё такое.

RomaS 2014

An object structure is represented as a pair of curly brackets

   surrounding zero or more name/value pairs (or members). A name is a
   string. A single colon comes after each name, separating the name
   from the value. A single comma separates a value from a following
   name. The names within an object SHOULD be unique.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для RomaS:

RFC 2119

SHOULD This word, or the adjective «RECOMMENDED», mean that there
may exist valid reasons in particular circumstances to ignore a
particular item, but the full implications must be understood and
carefully weighed before choosing a different course.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для alxt:

Работает. Но что-то в этом кривое. Хотя для web’а это привычно — там всё такое.

Мда? Ну вот, скажем, Perl был разработан не для веба, а Си весь вызывает ощущение кривизны.

alxt 2014

Я ж не говорю, что всё, что не для веба- хорошо :D

SunChaser (sunchaser.info) 2014

Проверил — это так же норм жуётся парсерами Yaml в Ruby и PHP (ext-yaml)
Неправильно работает парсер Yaml в Symfony, но он и в целом плохо парсит JSON

(т. к. json подмножество yaml, неплохо бы не терять эту совместимость)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для sunchaser.info:

Симфони поддерживает Yaml 1.2 же, а JSON — это Yaml 2.0, если не ошибаюсь.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для alxt:

Ну так и утверждение «там всё такое» тоже неверно.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для sunchaser.info:

Симфони поддерживает Yaml 1.2 же, а JSON — это Yaml 2.0, если не ошибаюсь.

Ошибаюсь, 2.0 не существует, как раз с 1.2 перепутал.

SunChaser (sunchaser.info) 2014

Комментарий для Евгения Степанищева:

ага, но там даже не Yaml 1.2, а «selected subset of features» из Yaml 1.2

kxepal 2014

Это все равно решение из разряда «костыль» т. к. стандарт JSON ничего не говорит о том, как обрабатывать дубликаты ключей в объекте. YAML например, прямо и явно их запрещяет. Это ведет к неопределенному поведению в различных библиотеках, которые могут совершенно спокойно взять первый ключ «с комментарием» и выбросить все остальные «с данными» — и это поведение будет совершенно корректно с точки зрения стандарта. Так же некоторые JSON парсеры раскладывают object не в hash-map, а в список ключ-значение сохраняя тем самым и «комментарий» и «значение» перекладывая на пользователя ответственность за дубликаты ключей — это поведение так же совершенно корректо с точки зрения стандарта.

Закладываться на такое поведение не рекомендуется — можно в один прекрасный момент наткнуться на интересные баги. Если нужны комментарии в JSON, возможно стоит обратить внимане на другие форматы. Или остановиться на том же YAML (для которого, кстати, JSON не является полным подмножеством — там есть пограничные случаи когда JSON не будет соответствовать стандарту YAML).

Евгений Степанищев (bolknote.ru) 2014

Комментарий для kxepal:

Всегда обход ограничений чего-либо — это костыль, разумеется, как иначе? Но вы так говорите, как будто «джейсон» — формат данный свыше. Вот есть решение, которое малой кровью добавляет комментарии. Осталось его правильно оформить и закинуть на json.org, пусть сделают новый формат JSON 1.1 — тот же JSON, но с комментами. Потому что другие решения явно хуже.

Да и проблем, описанных вами, я не вижу. Очевидно же (или нет?), что формат с комментариями нужен не для обмена (зачем там комментарии?), а я для хранения (конфигов и чего-то близкого к ним), а это значит парсить его будут управляемый нами код. Что я сделал в подобных случаях, я описал — в одном случае расширил JSON обычными JS-комментариями (это удобно, но совершенно несовместимо с текущим форматом), в другом — завёл «мусорные» ключи (ужасно неудобно, но 100% совместимо). Решение с двойными ключами находится посередине — (вероятно) полностью совместимо, но не так удобно, как отдельные комментарии.

kxepal 2014

Комментарий для Евгения Степанищева:

Очевидно же (или нет?), что формат с комментариями нужен не для обмена (зачем там комментарии?), а я для хранения (конфигов и чего-то близкого к ним), а это значит парсить его будут управляемый нами код.

В таком случае возникает вполне логичный вопрос: «а зачем нам JSON?». Или перефразируя: «а зачем нам втыкать костыли в формат, не предназначенный для конфигов?». Есть много других замечательным форматов для конфигурации как то YAML, INI, TOML...XML в конце концов. Тем более, что парсить его будет управляемый нами код. Понимаю, что адаптировать любимый инструмент под случай, на который он не рассчитывался, всегда интересно, но это редко когда заканчивается чем-то хорошим.

Иван 2014

На мой взгляд, конфиги лучше хранить в старом добром ини-файле. Там и секции, и комментарии. Джейон лучше использовать для передачи файлов.
И никто не гарантирует, что в каком-то языке библиотека парсинга будет брать первое поле и пропускать последующие дубли.

Иван 2014

*для передачи данных

Евгений Степанищев (bolknote.ru) 2014

Комментарий для kxepal:

Не люблю зоопарки, в том числе и форматов. JSON хорош как для конфигурирования, так и для обмена данными. INI для обмена данными плох, YAML сложный, XML ужасен, что такое TOML я не знаю и, видимо, знать этого мне не нужно.

Понимаю, что адаптировать любимый инструмент под случай, на который он не рассчитывался, всегда интересно, но это редко когда заканчивается чем-то хорошим.

Да что тут адаптировать? Оно ж само работает. Причём понятно каждому, кто хоть раз писал парсер, почему оно так работает.

Иван 2014

INI для обмена данными плох

я и не предлагал обмениться данными в этом формате. Ини -​-​ настройки, джейсон -​-​ транспорт.
В этом случае не придется изобретать костыли.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Мда? Как же хранить в ini-файле произвольные массивы?

Иван 2014

Комментарий для Евгения Степанищева:

some.ini

[mysection]
myarray = 1 2 3 4 5

some.code

myIni.read(’/path/to/file’)
myIni.getIntArray(’mysection’, ’myarray’)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

То есть никак? Вот у меня есть такой массив в джейсоне, например:
{«APM»: [1,3], «other»: [8], «KKM»: [5,2]}, что с ним делать? А бывают структуры и ещё более сложные.

Иван 2014

Комментарий для Евгения Степанищева:

Что значит никак? Ини файл предполагает упрощение структуры данных.
В вашем случае в ини-файле будет секция [foo] и три поля: APM = 1 3, other = 8, KKM = 5 2
Сложные древовидные структуры очень плохо поддаются редактированию человеком. Добавить новое поле в объект на третьем уровне сложности становится реально тяжело. Про перестановки с места на место вообще не говорю. Настройки нужно упрощать, сложные структуры проводить к линейному виду.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Я вот смотрю на конфиг нашего проекта (он в «джейсоне»), там аж до 7 уровня сложности есть и не ощущаю ровно никаких сложностей в его редактировании. Не вижу никакого смысла приводить нормальные сложные структуры, где ещё и типы сразу видно, к плоским ini-файлам.

Иван 2014

Комментарий для Евгения Степанищева:

Конфиг в 7 уровней кажется вам удобным только потому, что вы с ним каждый день работаете.
И вы не можете вписать в него комментарий нормальным способом. Как разбирать этот ад без коментов -​-​ загадка. Если делать это дублирование полей, то такой комментарий не будет выделен цветом и его трудно увидеть. И можно случайно переставить порядок ключей при редактировании и зафакапиться.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Как разбирать этот ад без коментов — загадка

По документации? На ад, кстати, он совсем не похож. Кроме того, в нашем конфиге комментарии всё же есть — мы расширили JSON.

Иван 2014

Комментарий для Евгения Степанищева:

мы расширили JSON.

Вок к каким велосипедам можно прийти, если использовать формат не по назначению.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Вы так говорите, как будто велосипеды — это плохо. Альтернативы-то всё одно нет.

Иван 2014

Комментарий для Евгения Степанищева:

Конечно плохо. Вместо того, чтобы упростить структуру конфига, вы выбрали не самый подходящий формат + кастомная работа с ним. Больше кода, больше энтропии, больше времени нужно новичку для адаптации.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Да ничего подобного. Новички читают этот конфиг, даже не замечая, что комментариев там быть не должно вообще-то. ini-файлы это уж точно не формат, подходящий для конфига, потому что у него нет типов данных, у него есть интерпретация и она зашита в коде, не читая код, нельзя сказать что перед нами.

Вы мне предложили сделать буквально следующее:

В вашем случае в ини-файле будет секция [foo] и три поля: APM = 1 3, other = 8, KKM = 5 2

Это одни из самых зверский костылей, которые я только видел. Во-первых, секция должна быть вложена тут в другую секцию, а та — в свою, это будет очень нечитаемо, во-вторых, неясно что перед нами: массив чисел? Строка? Как перемешать в массив числа, строки, булевы значения и null? Думаю, что вы просто предло́жите какое-то соглашение, которое будет интерпретироваться на уровне кода. JSON туда куда лучше.

Иван 2014

Комментарий для Евгения Степанищева:

в джейсоне тоже ограниченная система типов. в реальном проекте вам потребуется хранить даты, регулярки. Например, чтобы получился datetime, нужно нужно распарсить строку, а для регулярки получить скомпилленый объект.

Во-первых, секция должна быть вложена тут в другую секцию, а та — в свою

зачем вкладывать? у вас есть какой-то словарь, ему можно присвоить имя и записать в секцию. делайте данные линейными. ваша вложеность на 7 уровней -​-​ дело только ваших рук. Вы в курсе, что питоноский LoggingDict может быть свободно описан в ини-файле?

Как перемешать в массив числа, строки, булевы значения и null?

я коров с лошадьми не мешаю. Это явно неудачный кейс.

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Ну, у меня проект реальный. Я описываю с тем, с чем сталкиваюсь.

зачем вкладывать? у вас есть какой-то словарь, ему можно присвоить имя и записать в секцию.

Потому что есть модули, в них — другие модули. Конфиг отражает эту вложенность. Плоский конфиг ничего не отражает.

я коров с лошадьми не мешаю. Это явно неудачный кейс.

В реальной жизни null запросто мешается с остальными типами, это очень частый случай (а не «кейс»).

Иван 2014

Комментарий для Евгения Степанищева:

Плоский конфиг ничего не отражает.

заведите доменную систему имен, типа app.module.menu и будет все норм

В реальной жизни null запросто мешается с остальными типами

массив, в котором все до кучи -​-​ числа, строки, нулл -​-​ это какой-то нонсенс. Смешанные типы могут пригодиться разве что в jsonschema для проверки входных данных (в отдельном файле)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

заведите доменную систему имен, типа app.module.menu и будет все норм

Вы упорно сопротивляетесь нормальному формату, сочиняя костыли на ходу.

массив, в котором все до кучи — числа, строки, нулл — это какой-то нонсенс

Так давайте рассмотрим более простой вариант: однородные скаляры перемешанные с null

Иван 2014

myarray = 0 1 2 null 3 null 4 5
в питоне это парсится в [0, 1, 2, None, 3, None, 4, 5] двумя строчками (свой метод в ConfigParser)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Иван:

Я не хочу уже спорить. Мне не нравится идея не знать на уровне конфига что значат какие-то данные и придавать из значение внутри кода.

Николай 2015

json — сам по себе убогий костыль и одна их худших вещей, которые могли случиться. То же касается разнообразных ямлов и подобного. На фоне этой истории к xml претензий нет, он вполне.

Евгений Степанищев (bolknote.ru) 2015

Комментарий для Николай:

Можно ли услышать какие-то аргументы?

Dorjy 2020

oracle 12.1 будет читать первое значение:
select json_value(’{«api_host» : «The hostname of your API server. You may also specify the port.», «api_host» : «hodorhodor.com»}’, ’$.api_host’) from dual

——
The hostname of your API server. You may also specify the port.

Евгений Степанищев 2020

Досадно, но что делать…