Комментарии в формате 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]
}
Он полностью синтаксически верен и разбирается правильным образом во всех языках, по всей видимости.
Работает. Но что-то в этом кривое. Хотя для web’а это привычно- там всё такое.
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.
Комментарий для RomaS:
RFC 2119
Комментарий для alxt:
Мда? Ну вот, скажем, Perl был разработан не для веба, а Си весь вызывает ощущение кривизны.
Я ж не говорю, что всё, что не для веба- хорошо :D
Проверил — это так же норм жуётся парсерами Yaml в Ruby и PHP (ext-yaml)
Неправильно работает парсер Yaml в Symfony, но он и в целом плохо парсит JSON
(т. к. json подмножество yaml, неплохо бы не терять эту совместимость)
Комментарий для sunchaser.info:
Симфони поддерживает Yaml 1.2 же, а JSON — это Yaml 2.0, если не ошибаюсь.
Комментарий для alxt:
Ну так и утверждение «там всё такое» тоже неверно.
Комментарий для sunchaser.info:
Ошибаюсь, 2.0 не существует, как раз с 1.2 перепутал.
Комментарий для Евгения Степанищева:
ага, но там даже не Yaml 1.2, а «selected subset of features» из Yaml 1.2
Это все равно решение из разряда «костыль» т. к. стандарт JSON ничего не говорит о том, как обрабатывать дубликаты ключей в объекте. YAML например, прямо и явно их запрещяет. Это ведет к неопределенному поведению в различных библиотеках, которые могут совершенно спокойно взять первый ключ «с комментарием» и выбросить все остальные «с данными» — и это поведение будет совершенно корректно с точки зрения стандарта. Так же некоторые JSON парсеры раскладывают object не в hash-map, а в список ключ-значение сохраняя тем самым и «комментарий» и «значение» перекладывая на пользователя ответственность за дубликаты ключей — это поведение так же совершенно корректо с точки зрения стандарта.
Закладываться на такое поведение не рекомендуется — можно в один прекрасный момент наткнуться на интересные баги. Если нужны комментарии в JSON, возможно стоит обратить внимане на другие форматы. Или остановиться на том же YAML (для которого, кстати, JSON не является полным подмножеством — там есть пограничные случаи когда JSON не будет соответствовать стандарту YAML).
Комментарий для kxepal:
Всегда обход ограничений чего-либо — это костыль, разумеется, как иначе? Но вы так говорите, как будто «джейсон» — формат данный свыше. Вот есть решение, которое малой кровью добавляет комментарии. Осталось его правильно оформить и закинуть на json.org, пусть сделают новый формат JSON 1.1 — тот же JSON, но с комментами. Потому что другие решения явно хуже.
Да и проблем, описанных вами, я не вижу. Очевидно же (или нет?), что формат с комментариями нужен не для обмена (зачем там комментарии?), а я для хранения (конфигов и чего-то близкого к ним), а это значит парсить его будут управляемый нами код. Что я сделал в подобных случаях, я описал — в одном случае расширил JSON обычными JS-комментариями (это удобно, но совершенно несовместимо с текущим форматом), в другом — завёл «мусорные» ключи (ужасно неудобно, но 100% совместимо). Решение с двойными ключами находится посередине — (вероятно) полностью совместимо, но не так удобно, как отдельные комментарии.
Комментарий для Евгения Степанищева:
В таком случае возникает вполне логичный вопрос: «а зачем нам JSON?». Или перефразируя: «а зачем нам втыкать костыли в формат, не предназначенный для конфигов?». Есть много других замечательным форматов для конфигурации как то YAML, INI, TOML...XML в конце концов. Тем более, что парсить его будет управляемый нами код. Понимаю, что адаптировать любимый инструмент под случай, на который он не рассчитывался, всегда интересно, но это редко когда заканчивается чем-то хорошим.
На мой взгляд, конфиги лучше хранить в старом добром ини-файле. Там и секции, и комментарии. Джейон лучше использовать для передачи файлов.
И никто не гарантирует, что в каком-то языке библиотека парсинга будет брать первое поле и пропускать последующие дубли.
*для передачи данных
Комментарий для kxepal:
Не люблю зоопарки, в том числе и форматов. JSON хорош как для конфигурирования, так и для обмена данными. INI для обмена данными плох, YAML сложный, XML ужасен, что такое TOML я не знаю и, видимо, знать этого мне не нужно.
Да что тут адаптировать? Оно ж само работает. Причём понятно каждому, кто хоть раз писал парсер, почему оно так работает.
я и не предлагал обмениться данными в этом формате. Ини -- настройки, джейсон -- транспорт.
В этом случае не придется изобретать костыли.
Комментарий для Иван:
Мда? Как же хранить в ini-файле произвольные массивы?
Комментарий для Евгения Степанищева:
some.ini
[mysection]
myarray = 1 2 3 4 5
some.code
myIni.read(’/path/to/file’)
myIni.getIntArray(’mysection’, ’myarray’)
Комментарий для Иван:
То есть никак? Вот у меня есть такой массив в джейсоне, например:
{«APM»: [1,3], «other»: [8], «KKM»: [5,2]}, что с ним делать? А бывают структуры и ещё более сложные.
Комментарий для Евгения Степанищева:
Что значит никак? Ини файл предполагает упрощение структуры данных.
В вашем случае в ини-файле будет секция [foo] и три поля: APM = 1 3, other = 8, KKM = 5 2
Сложные древовидные структуры очень плохо поддаются редактированию человеком. Добавить новое поле в объект на третьем уровне сложности становится реально тяжело. Про перестановки с места на место вообще не говорю. Настройки нужно упрощать, сложные структуры проводить к линейному виду.
Комментарий для Иван:
Я вот смотрю на конфиг нашего проекта (он в «джейсоне»), там аж до 7 уровня сложности есть и не ощущаю ровно никаких сложностей в его редактировании. Не вижу никакого смысла приводить нормальные сложные структуры, где ещё и типы сразу видно, к плоским ini-файлам.
Комментарий для Евгения Степанищева:
Конфиг в 7 уровней кажется вам удобным только потому, что вы с ним каждый день работаете.
И вы не можете вписать в него комментарий нормальным способом. Как разбирать этот ад без коментов -- загадка. Если делать это дублирование полей, то такой комментарий не будет выделен цветом и его трудно увидеть. И можно случайно переставить порядок ключей при редактировании и зафакапиться.
Комментарий для Иван:
По документации? На ад, кстати, он совсем не похож. Кроме того, в нашем конфиге комментарии всё же есть — мы расширили JSON.
Комментарий для Евгения Степанищева:
Вок к каким велосипедам можно прийти, если использовать формат не по назначению.
Комментарий для Иван:
Вы так говорите, как будто велосипеды — это плохо. Альтернативы-то всё одно нет.
Комментарий для Евгения Степанищева:
Конечно плохо. Вместо того, чтобы упростить структуру конфига, вы выбрали не самый подходящий формат + кастомная работа с ним. Больше кода, больше энтропии, больше времени нужно новичку для адаптации.
Комментарий для Иван:
Да ничего подобного. Новички читают этот конфиг, даже не замечая, что комментариев там быть не должно вообще-то. ini-файлы это уж точно не формат, подходящий для конфига, потому что у него нет типов данных, у него есть интерпретация и она зашита в коде, не читая код, нельзя сказать что перед нами.
Вы мне предложили сделать буквально следующее:
Это одни из самых зверский костылей, которые я только видел. Во-первых, секция должна быть вложена тут в другую секцию, а та — в свою, это будет очень нечитаемо, во-вторых, неясно что перед нами: массив чисел? Строка? Как перемешать в массив числа, строки, булевы значения и null? Думаю, что вы просто предло́жите какое-то соглашение, которое будет интерпретироваться на уровне кода. JSON туда куда лучше.
Комментарий для Евгения Степанищева:
в джейсоне тоже ограниченная система типов. в реальном проекте вам потребуется хранить даты, регулярки. Например, чтобы получился datetime, нужно нужно распарсить строку, а для регулярки получить скомпилленый объект.
зачем вкладывать? у вас есть какой-то словарь, ему можно присвоить имя и записать в секцию. делайте данные линейными. ваша вложеность на 7 уровней -- дело только ваших рук. Вы в курсе, что питоноский LoggingDict может быть свободно описан в ини-файле?
я коров с лошадьми не мешаю. Это явно неудачный кейс.
Комментарий для Иван:
Ну, у меня проект реальный. Я описываю с тем, с чем сталкиваюсь.
Потому что есть модули, в них — другие модули. Конфиг отражает эту вложенность. Плоский конфиг ничего не отражает.
В реальной жизни null запросто мешается с остальными типами, это очень частый случай (а не «кейс»).
Комментарий для Евгения Степанищева:
заведите доменную систему имен, типа app.module.menu и будет все норм
массив, в котором все до кучи -- числа, строки, нулл -- это какой-то нонсенс. Смешанные типы могут пригодиться разве что в jsonschema для проверки входных данных (в отдельном файле)
Комментарий для Иван:
Вы упорно сопротивляетесь нормальному формату, сочиняя костыли на ходу.
Так давайте рассмотрим более простой вариант: однородные скаляры перемешанные с null
myarray = 0 1 2 null 3 null 4 5
в питоне это парсится в [0, 1, 2, None, 3, None, 4, 5] двумя строчками (свой метод в ConfigParser)
Комментарий для Иван:
Я не хочу уже спорить. Мне не нравится идея не знать на уровне конфига что значат какие-то данные и придавать из значение внутри кода.
json — сам по себе убогий костыль и одна их худших вещей, которые могли случиться. То же касается разнообразных ямлов и подобного. На фоне этой истории к xml претензий нет, он вполне.
Комментарий для Николай:
Можно ли услышать какие-то аргументы?
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.
Досадно, но что делать…