Дальше я расскажу почему мне не очень нравится JSON для некоторых применений и как, по моему мнению, его можно доработать, чтобы стало немного удобнее.
Сразу должен отметить, что не рассматриваю KTV, как замену JSON. И ни в коем случае не рассматриваю его для использования в JavaScript'е. Это будет неудобно и неправильно. С другой стороны, ситуация, когда система описания объектов JavaScript'а используется в других языках для работы с типизированными данными — тоже странная, и её хочется поправить.
Если вам интересно, что послужило изначальной причиной создания такого формата — можно почитать про S2 и про причины создания формата KTV
Плюсы и минусы JSON
Сам по себе JSON хороший.
- Простой. Отдельное спасибо за отсутствие комментариев. Если бы их оставили, туда начали бы пихать мета-информацию (потому что JSON бедный очень) и нотация быстро превратилась бы в помойку.
- Выразительный. Не так, как XML, но достаточно для широкого круга задач.
- Сильно короче в записи, чем XML.
Болезни JSON'а растут из истории создания. Нужно было, чтобы
eval(что-то)
в JavaScript'е выдавал объект, с которым удобно работать. К сожалению, eval'ы в других языках работают обычно по-другому (если вообще присутствуют), и сами языки отличаются. Используя этот формат с не-JavaScript, видны его недостатки.- Кавычки. По стандарту (в качестве стандарта я беру текст отсюда: http://www.json.org) все названия должны заключаться в кавычки. Если мы используем JSON, например, для передачи данных по сети, то это излишество.
- Отсутствие типизации. Точнее, типы есть, но всего строка/число/true/false/null. И объекты с массивами. Всё. Ни дат, ни целых/дробных чисел нет. Нет возможности пометить типами объекты, чтобы проще было модель понять.
- Отсутствие стандартов записи объектов. Это, например, может привести к массивам со смешанными объектами внутри. Когда приходится такое парсить — становится больно.
- Отсутствие ссылок. Если в JSON записывать иерархическую структуру объектов, то регулярно встречаются ссылки на один и тот же объект из разных мест. JSON не даёт возможности сослаться на первое использование. Нужно либо что-то придумывать, либо повторять объекты целиком, что плохо сказывается на размере структуры и на сложности парсинга.
Я попробовал найти формат, который бы работал как JSON (в идеале — являлся бы его надмножеством), но при этом умел стандартным образом решать часть или все перечисленные выше проблемы. Нашёл YAML.
YAML
YAML хорош. Он почти надмножество JSON, хорошо определён (http://www.yaml.org/spec/1.2/spec.html), легко читается. Но вместе с тем:
- вложенность структур определяется отступами. В случае с передачей информации удобна была бы возможность их убирать. Любителей табуляции также не уважают, отступы делаются только пробелами.
- YAML излишне сложный. Большое количество специальных символов, договоренности о структуре делают поддержку YAML'а сложной, парсеры — сложными (YAML 1.2 появился уже 7 лет как, но, согласно новостям с сайта, парсеры до сих пор поддерживают в основном предыдущие версии).
В общем, YAML «не пошёл». Кроме него популярных форматов я не знаю. Если вдруг вы знаете — пишите, с удовольствием посмотрю.
Пришло время представить то, что получилось у меня.
KTV
Key-Type-Value. Я так называю этот формат.
Напишем простой объект на JSON'е:
{
"name": "Alex",
"coolness": 3.1415,
"isAProgrammer": true
}
Если переписать это же на KTV, получится:
{
name: Alex
coolness: 3.1415
isAProgrammer: true
}
Казалось бы, ничего не поменялось. Но давайте присмотримся:
- убрались кавычки. Они теперь обязательны только, если в ключе или значении есть нетривиальная строка. Обычные ситуации кавычек не требуют.
- убрались запятые в конце строк. Их (или точки с запятой) можно ставить, если пишем несколько определений в одну строку. Но в традиционной записи их можно опустить.
И первый и второй примеры — оба являются корректными KTV-файлами. Это очень важно, так как позволяет в KTV-парсер запихнуть стандартный JSON, и он его съест с удовольствием. А можно воспользоваться удобствами, типами например.
Где же тут типы? В вышеприведенном случае типы выводятся из значений. «Alex» — это строчный литерал, значит тип name'а — «string», coolness'у достался «double», а isAProgrammer — «bool». Кроме этих типов есть ещё «null» (он же «nil», привет коллегам), «int» и «color». Давайте запишем этот же пример, но полностью указывая типы:
{
name(string): Alex
coolness(double): 3.1415
isAProgrammer(bool): true
}
Никуда не делись и вложенные объекты/массивы. С дополнениями:
- И у объектов и у массивов могут быть типы. Для массива тип — это общий тип каждого объекта в массиве. Нельзя положить два разных типа (строка/число, или разные объекты) в массив.
- Тип массива или объекта также может указывать на класс, к которому этот объект или массив необходимо привести при парсинге. Например:
property(font): {name:..., size:...}
может обозначать объект-шрифт, а
property(point): [2, 3]
может при распознавании быть преобразовано в объект-точку. В принципе, точку можно сделать также объектом (с пропертями x, y), но зачем? Мы ведь и так знаем, что там где.
Присутствуют сылки, возможность сослаться на любую другую пропертю. Как-то так:
{
property: value
another: @property
}
Это позволяет сократить запись некоторых ветвистых объектов, или просто задавать константы, обращаясь к ним в других частях файла. Первая возможность полезна в REST'е, если структура объектов нетривиальная. Вторая — в конфигурационных файлах.
Идея миксинов также позаимствована из других языков. Они позволяют проперти одного объекта (или объектов) приделать другому объекту. Можно представить себе это, как наследование, но наследование — это из ООП, там поведение наследуется, тут только свойства. Так что лучше смешивать.
{
object: { a: ..., b: ...}
object2(+object): { c: ..., d: ... }
}
В данном примере объект object2 будет после парсинга содержать как свои проперти, так и проперти из объекта object. То есть, a, b, c и d.
Комментарии
Я не удержался и организовал комментарии. Их можно задавать либо при помощи //, либо #. Оба типа комментариев — строчные, блочных комментариев нет.
Со вторым типом (который октоторповый) есть небольшая проблема. Дело в том, что литерал цвета предполагается задавать как-раз в стандартном CSS-виде, то есть #rrggbbaa, что конфликтует с текущим вариантом записи комментариев. Так что, возможно, в будущем останется только С-вариант (//).
Работа с форматом
Для меня этот формат, и парсер этого формата нужен в двух местах:
- чтобы хранить стилевую информацию в S2 (развитие Angstrom Style System)
- чтобы можно было улучшить общение между своими собственными серверами на Swift и приложениями на нём же.
Поэтому я рассматриваю работу с форматом именно для Swift'а. В принципе, формат достаточно простой и не должно быть сложно организовать его парсер в любом языке.
Проблемы Swift сейчас не позволяют сделать по-настоящему правильную загрузку/сериализацию объектов из/в KTV. Поэтому процесс работы с форматом предполагается следующий:
- Создаются модельные классы. Как обычные классы (в том числе аннотированные
@objc
) или структуры. Каждый класс должен реализовывать протоколKTVGenerated
. - При помощи генератора создаются расширения к этим классам, которые умеют загружать и сериализовывать объекты.
- Используем сгенерированное для преобразования KTV в объекты и обратно (или в/из JSON).
Модельная структура может выглядеть, например, так:
public struct RootObject: KTVGenerated {
var string:String // просто пропертя
var stringOrNil:String? // optional
var int:Int // числа
var stringArray:[String] // массивы
var stringDictionary:[String:Int] // ассоциативные массивы
var object:ChildObject? // ссылка на другой модельный класс
private var _privateString:String // эта пропертя не будет сериализовываться
private(set) var _privateSetString:String // эта — тоже
let _constantString:String // и константы тоже не сериализуем
}
Видно, что поддерживаются все основные фичи:
- Типы (они должны прописываться явно, чтобы генератор смог корректно создать расширения классов.
- Коллекции (массивы и ассоциативные массивы).
- Иерархии объектов (ссылки на другие модельные классы)
- Проперти, которые не участвуют в сериализации (это константы, приватные проперти и проперти с приватными сеттерами)
История создания такого парсера — тема для отдельной статьи. Задачи одновременного поддержания структур и классов, способы задания атрибутов без наличия самих атрибутов, использование SourceKit'а вместо рефлекшна для анализа файлов, необходимость кастомных мапперов (например, для парсинга нестандартных дат). Сам парсер сейчас в процессе активной разработки, и постоянно меняется. Но, если будет достаточный интерес — выложу рабочий проект, обсудим варианты.
Польза от очередного формата данных
https://xkcd.com/927/
JSON — так себе формат для передачи данных между приложениями. Это — формат описания объектов в JavaScript'е. А мы постоянно используем его для совершенно других целей, впихивая и по делу и без дела.
Мне кажется, пора переходить к чему-то, более приспособленному к выполняемым задачам. К новому Swift'у с его строгой типизацией, к сложным структурам данных, которые часто приходится передавать на мобильные клиенты с сервера. Нужен формат, синтаксис которого можно будет проверять в IDE, в процессе компиляции и при парсинге. И, мне кажется, что KTV, не меняя кардинально структуру, практически не усложняя уже известный формат, добавляет несколько удобных мелочей.
Выкладываю экспериментальный исходный код с небольшим описанием. Там больше интересны приёмы для парсинга формата на Swift, нежели парсер. Но вдруг кому интересно будет посмотреть: http://github.com/bealex/KTV
Скорее всего, я много чего упустил или не учёл. Может, у кого-то есть похожий опыт? Пишите в комментариях, или в почту: alex@jdnevnik.com, обсудим!
Комментарии (111)
rayz_razko
08.03.2016 12:11+1Автор пишет об излишестве кавычек (что ошибочно, ибо JSON позволяет в качестве ключа использовать {'i am a key': 'value'}), но в то же время хочет добавлять типы. Странно как-то.
bealex
08.03.2016 12:14Кавычки в KTV допустимы, как раз для таких случаев.
rayz_razko
08.03.2016 12:19+1Если мы используем JSON, например, для передачи данных по сети, то это излишество.
Я вот об этом. Вас беспокоят лишние байты, но в то же время Вы говорите о том, что нужно передавать типы.bealex
08.03.2016 12:25Типы передавать необязательно, это я тоже отметил. Также, байтики нужно не только передавать, но и писать.
Ещё строки не способствуют строгой типизации. Внутри JSON'а — часто объекты, объекты, которые парсятся в строго-типизированные языки. Строки маппить в строготипизированный язык тяжело (посмотрите, сколько парсеров JSON'а существует). Мне кажется, что если зажать этот формат несколько сильнее, то его проще будет редактировать, проще контролировать и работать с ним. Не в любом контексте, а в том, который я указываю.rayz_razko
08.03.2016 12:30+2Если JSON объект парсится в строго типизированном языке, то, скорее всего, существует контракт(модель). Т.е. принимающая сторона ожидает, что в проперти "name" будет лежать строка, а в проперти "age" целочисленный тип данных.
bealex
08.03.2016 12:38Это правда, но часто модель приходится делать уже по существующим JSON'ам (которые делались для сайта, например), поэтому изначально контракты отсутствовали, что приводит к очень, очень сложным моделям для парсинга. Как я отметил в статье, например, разные типы кладутся в коллекции или названия полей недопустимы для целевого языка.
Опять же, ссылки на объекты часто бывают полезны.rayz_razko
08.03.2016 12:42+3то приводит к очень, очень сложным моделям для парсинга
Это уже проблема не JSON-аbealex
08.03.2016 12:46-2Конечно, это моя проблема, как разработчика. Про это я и говорю.
esc
08.03.2016 17:54+2А чем поможет ваш формат, если типы все равно не будут указаны (они ж не обязательны) ?
bealex
08.03.2016 18:04-1Если не указывать типы, это просто чуть более короткий JSON, что приятно. Плюс, стандартные типы выведутся из типов литералов.
zagayevskiy
08.03.2016 18:27+4А потом вы будете искать баги в своём парсере? Более короткий JSON? Зачем?
bealex
08.03.2016 12:16+2Впрочем, использование такого значения в качестве ключа — порочная практика, так как не маппится нормально на разные языки программирования. Поэтому, всё же, ключи обычно — это строки без пробелов, и этот случай хочется сделать проще в написании.
rayz_razko
08.03.2016 12:20+1Вопрос не в хорошей\плохой практике, а в спецификации.
bealex
08.03.2016 12:25Какую спецификацию вы имеете в виду?
rayz_razko
08.03.2016 12:39+1Эту: An object structure is represented as a pair of curly bracket tokens surrounding zero or more name/value pairs. A name is a string.
bealex
08.03.2016 12:46А как эта спецификация относится к новому формату? :-)
rayz_razko
08.03.2016 12:55Вы утверждали, что кавычки излишни, я же возразил что это не так. При чем это было написано "между прочим". Суть комментария была совершенно другая.
Carduelis
08.03.2016 14:32+1Только двойные кавычки, прошу отметить. Иначе — ни один парсер не пропустит.
ainu
08.03.2016 12:31+1Это формат удобочитаемый или для передачи данных по сети в большом объёме/с большой скоростью/быстрое сжатие? Потому что с первым проблем нет, а со вторым существующие решения не иделальны.
bealex
08.03.2016 12:45Смотря что подразумевается под большой скоростью и быстрым сжатием. Сам формат для больших структур с повторяющимися объектами должен быть сильно проще и короче, чем JSON (за счёт ссылок, например). Он по-прежнему текстовый, в этом смысле он будет похож на JSON (не как Protocol Buffers, например), и будет сжиматься как обычный текст (типы, например, в случае использования сжатия, почти исчезнут, так как они одинаковые).
aikixd
08.03.2016 12:56+5Передача строк без кавычек бред. Каждый раз придется проверять на ключевые слова, раз. Два, это заряженый пистолет нацеленый в ногу. Рано или поздно вы просмотрите код запроса сами.
object: { a: ..., b: ...} object2(+object): { c: ..., d: ... }
Вот это зачем? И как программа должна догадываться когда это использовать? И что делать парсеру если вы поменяли свойства местами?
property: value another: @property
Как ссылаться на объекты ниже, выше по иерерхии или в другой ветке?
Типы указывать вообще не нужно. Для всех данных есть модель которая их описывает. Если модели нет, то программе не с чем работать.
Посмотрите вот это.bealex
08.03.2016 13:08-1Передача строк без кавычек бред.
Я так не считаю. Плюс, я не говорю, что кавычки не нужны, я говорю, что они обычно не нужны в именах пропертей.
И что делать парсеру если вы поменяли свойства местами?
Резолвить это, как и ссылки, после чтения файла.
Как ссылаться на объекты ниже, выше по иерерхии или в другой ветке?
Сейчас у меня ссылки идут от корня структуры, это решает все проблемы.
Типы указывать вообще не нужно. Для всех данных есть модель которая их описывает.
Это не всегда так, плюс, иногда бывает удобно саму модель строить по этому файлу (про это я выложу статью на днях). В этом случае типы — необходимая вещь. Если же есть модель, то типы можно не указывать, как и в JSON.aikixd
08.03.2016 13:41+2Я так не считаю. Плюс, я не говорю, что кавычки не нужны, я говорю, что они обычно не нужны в именах пропертей.
К названию свойств у меня нет притензий. Я про литерали строк ("Alex" => Alex).
Резолвить это, как и ссылки, после чтения файла.
Только вот зачем это? К тому-же что делать с коллизиями?
Сейчас у меня ссылки идут от корня структуры, это решает все проблемы.
Так себе решение.
Если уж хочется без свойств то лучше как-то так:
{ common#uniqueId: { ... }, object1: { prop: #uniqueId }, object2: { prop: #uniqueId } }
Это не всегда так, плюс, иногда бывает удобно саму модель строить по этому файлу (про это я выложу статью на днях). В этом случае типы — необходимая вещь. Если же есть модель, то типы можно не указывать, как и в JSON.
Если вы строите модель, по запросами которые пишите, у меня для вас плохие новости. Но даже если у вас в руках только одна сторона провода, вот вы сделали запрос и получили ответ:
{ data: (...) }
Во первых, как я узнаю тип корня? Во вторых, если было бы указано свойство data(Type), но у меня нету такого типа в проекте, как мне это поможет? Допустим я посмотрел запрос (нахрен такие апи) и по нему написал тип. Зачем мне теперь тип в ответе?
Допустим я получаю динамические данные в виде словаря:
// Плохой словарь { itemName1: { ... }, itemName2: { ... } } // Хороший словарь [ { key: "name1", value: { ... }, type: "Type1" }, { key: "name2", value: { ... }, type: "Type2" } ]
И тут мне не нужен тип.bealex
08.03.2016 13:48Если уж хочется без свойств то лучше как-то так:
Да, использование ID — один из способов. Но их сложно поддерживать (это не важно при автогенерации, важно при ручном написании).
И тут мне не нужен тип.
Да, я просто предлагаю внутри формата поддержку такого рода данных (если требуется). Завтра я опубликую статью про S2, станет чуть понятнее, как именно удобно этим пользоваться.
Во первых, как я узнаю тип корня?...
Это не та задача, для которой введены (повторюсь, необязательные) типы.
Но и тут они могут помочь. Например, приходил объект с типом A, а потом кто-то на сервере решил, что нужен в этом месте тип Б. Принимающая сторона может проверить тип и выдать сообщение об ошибке, вместо попытки впихнуть объект Б внутрь А.zagayevskiy
08.03.2016 16:23Зачем вы данные руками пишете?
vintage
08.03.2016 16:32А почему HTTP протокол текстовый?
zagayevskiy
08.03.2016 18:31Ну, наверное, потому, что в те времена, когда его придумывали, о контенте в сети думали как о гипертексте (hypertext transfer protocol). Ну, и с тех пор появилось мнение о том, что это неправильно. HTTP 2 — бинарный. А вы зачем спрашиваете? Тоже JSON руками пишете?
vintage
08.03.2016 20:38-2Будь HTTP изначально бинарным, то страницы мы сейчас открывали бы не по HTTPS, а по SFTP ;-) Сложное это дело, заниматься отладкой по бинарному протоколу.
bealex
08.03.2016 16:33Завтра будет статья про S2, там будет немного понятнее. Но вообще вопрос не совсем понятен. Что значит «зачем»? Иногда требуется для решения поставленной задачи. :)
zagayevskiy
08.03.2016 18:33Весьма интересно, что за задача по передаче данных, когда передаваемые данные руками пишутся.
bealex
08.03.2016 18:49:) Я старался не акцентировать внимание исключительно на передаче данных. Если вдруг где-то пропустил, покажите, пожалуйста. Передача данных — важная, но не единственная задача, решаемая такого рода форматами.
bealex
08.03.2016 13:29+1http://jsonapi.org/ знаю. Проект правильный, будет здорово, если им (или аналогичным, не важно каким, на самом деле) будут пользоваться.
corristo
08.03.2016 12:56Почему не искаробочный plist?
bealex
08.03.2016 13:08Он далеко не всегда удобен. Плюс, его совсем неудобно генерировать на стороне сервера.
corristo
08.03.2016 13:09Уверен, для популярных веб-фреймворков найдется готовый плагин plist-сериализатора.
bealex
08.03.2016 13:31Это правда, но его неудобство не только в отсутствии плагинов. Это XML (или вообще бинарь), что усложняет чтение и запись, удлинняет (в случае текста) формат. Он крайне ограничен по типам, и, в отличие от базового XML — не допускает расширения. Даже вложенные объекты им не записать.
Я пробовал его использовать в разных ситуациях и обычно это оказывалось неудобно.
lair
08.03.2016 12:58+6JSON — так себе формат для передачи данных между приложениями
Почему?
По стандарту [...] все названия должны заключаться в кавычки. Если мы используем JSON, например, для передачи данных по сети, то это излишество.
Почему это излишество? Это всего лишь более жесткое требование, упрощающее написание парсера.
Нет возможности пометить типами объекты, чтобы проще было модель понять.
А зачем аннотировать типы данных внутри формата передачи данных? Описывайте их в модели.
Отсутствие стандартов записи объектов.
Это банально неправда. JSON — и есть такой стандарт.
Это, например, может привести к массивам со смешанными объектами внутри.
Это позволяет массивы с разнотипными объектами. Это разве плохо?
Отсутствие ссылок.
Добавите ссылки — получите необходимость обработки циклов. Оно вам надо?bealex
08.03.2016 13:11-2Это банально неправда. JSON — и есть такой стандарт.
Он описывает то, как объекты записываются в JavaScript'е. В других языках, обычно, они описываются совершенно иначе. JSON это не учитывает никак.
Я не рассматриваю использование KTV для JavaScript'а, нужно это сразу было отметить, моя ошибка.
Это позволяет массивы с разнотипными объектами. Это разве плохо?
Да.
Добавите ссылки — получите необходимость обработки циклов. Оно вам надо?
А без ссылок — структуры разрастаются, порой, в несколько раз. Но есть и достоинства в обоих решениях.lair
08.03.2016 13:14-1Он описывает то, как объекты записываются в JavaScript'е.
Уже давно нет.
В других языках, обычно, они описываются совершенно иначе.
А кого это волнует? Язык передачи данных не должен учитывать, как описываются объекты на любой стороне, он должен быть совместимым с этими сторонами.
Это позволяет массивы с разнотипными объектами. Это разве плохо?
Да.
Ну то есть возможность написать "у меня есть: корабль, машина, квартира и восемнадцать тонн бриллиантов" — это плохо?
А без ссылок — структуры разрастаются, порой, в несколько раз.
А как же компрессия, про которую вы же выше писали?bealex
08.03.2016 13:21Уже давно нет.
:) В других языках в названиях полей класса допустимы пробелы? Отсутствуют типы кроме строк, чисел, булевого и null? Ой ли.
А кого это волнует? Язык передачи данных не должен учитывать, как описываются объекты на любой стороне, он должен быть совместимым с этими сторонами.
Ну, давайте вернемся к XML, он тоже всё описывает.
Ну то есть возможность написать «у меня есть: корабль, машина, квартира и восемнадцать тонн бриллиантов» — это плохо?
Абстрактно — хорошо. В реальности использовать такие структуры очень сложно.
А как же компрессия, про которую вы же выше писали?
Вы правы, при передаче данных это менее важно, зип такое сожмет отлично. Но есть и другие моменты.
При парсинге, в случае использования ссылок, можно использовать один и тот же объект в двух (двадцати) местах. Если же вы дублируете объекты в JSON'е, то и в памяти результирующие объекты будут разные. Это плохо.lair
08.03.2016 13:28+2В других языках в названиях полей класса допустимы пробелы?
В некоторых — да. Но, что важнее, то, как поля названы в передаваемой структуре, не обязано совпадать с тем, как поля названы на принимающей (или передающей) стороне. В некоторых языках, прямо скажем, и полей с классами может не быть, и что?
Отсутствуют типы кроме строк, чисел, булевого и null?
Важно, чтобы были строки, числа и далее по тексту. А то если пытаться охватить в нотации все возможные типы, вы никогда не закончите.
Ну, давайте вернемся к XML, он тоже всё описывает.
Во-первых, ваш логический скачок мне непонятен. XML как раз находится на другой стороне спектра — он очень сильно самодостаточен, в то время как JSON требует контекстной интерпретации.
Ну а во-вторых, XML, кроме избыточности и сложности парсинга ничем не плох, в нем как раз решается половина того, что вас не устраивает.
В реальности использовать такие структуры очень сложно.
Что сложного?
Если же вы дублируете объекты в JSON'е, то и в памяти результирующие объекты будут разные. Это плохо.
Смотря для чего. Ну и да, повторюсь, ссылки — это круто, но как только вы их добавляете, вы получаете необходимость подсчета циклов (а это само по себе мило) и придумывание правил для вывода этой красоты из исходного графа в дерево.bealex
08.03.2016 13:36… Важно, чтобы были строки, числа и далее по тексту. А то если пытаться охватить в нотации все возможные типы, вы никогда не закончите.
Это правда. Но я и не пытаюсь это сделать.
Я завтра опубликую статью про S2, где будет понятно, зачем я составляю такую структуру.
Что сложного?
Сложность в интерпретации и контроле за такими объектами. В Swift (я пишу именно в контексте этого языка) строгая типизация, коллекции — однотипные. Такие структуры тяжело туда впихнуть.
Далее, сложность в использовании. Требуется обрабатывать массивы (мы сейчас про массивы, не про объекты, где типы у разных полей — разные) специальными средствами, простое итерирование не подходит. Нужно не забыть, что вдруг может попасться другой объект.
Также такое поведение иногда провоцирует на откровенно неправильные структуры, например, когда массивы используются вместо объектов (по первому индексу — ID, потом имя, потом фамилия...).lair
08.03.2016 13:46Я завтра опубликую статью про S2, где будет понятно, зачем я составляю такую структуру.
Ну то есть вы, на самом деле, пишете язык под конкретную задачу, но при этом вам не нравится JSON как формат передачи данных.
В Swift (я пишу именно в контексте этого языка) строгая типизация, коллекции — однотипные.
А я пишу на языках со строгой типизацией, где коллекции не однотипны (точнее, полиморфны). Зачем мне ваше искусственное ограничение?
Требуется обрабатывать массивы (мы сейчас про массивы, не про объекты, где типы у разных полей — разные) специальными средствами, простое итерирование не подходит.
Почему не подходит? Идете по коллекции итератором, дальше для каждого объекта принимаете решение. Вплоть до динамической диспетчеризации.bealex
08.03.2016 13:52+1Ну то есть вы, на самом деле, пишете язык под конкретную задачу, но при этом вам не нравится JSON как формат передачи данных.
Я дополнил текст статьи. Как я уже сказал, я не рассматриваю KTV, как замену JSON. И не хочу, чтобы его использовали при работе с JavaScript'ом, это было бы глупо. Но я считаю, что JSON — не самый хороший вариант описания структур данных, в том числе и при их передаче.
Зачем мне ваше искусственное ограничение?
Видимо, вам оно не требуется. :-)
Почему не подходит?
Потому что в общем случае объекты в таком массиве могут быть любые. Динамическая диспетчеризация — это хорошо, но что, если с сервера в массиве начнет приходить тип, который не обрабатывается на клиенте? Появляется обработка ошибок, возможность сломаться в любой момент. Модель данных, наоборот, обычно старается зафиксировать формат передачи. А вы предлагаете его отпустить в свободное изменение и написать принимающей стороне хитрую динамическую логику. Не спорю, такие задачи тоже существуют, но их крайне мало и да, для таких задач JSON подойдёт лучше.lair
08.03.2016 14:39+2Но я считаю, что JSON — не самый хороший вариант описания структур данных, в том числе и при их передаче.
Но так и не указали на какой-нибудь "фундаментальный недостаток".
Динамическая диспетчеризация — это хорошо, но что, если с сервера в массиве начнет приходить тип, который не обрабатывается на клиенте?
А так будет рано или поздно, поэтому на эту ситуацию все равно надо заложиться.
Появляется обработка ошибок, возможность сломаться в любой момент.
А так вы обработку ошибок не пишете?
Модель данных, наоборот, обычно старается зафиксировать формат передачи.
И что делать, когда переданные данные не соответствуют этому формату?
А вы предлагаете его отпустить в свободное изменение и написать принимающей стороне хитрую динамическую логику.
Нет, не предлагаю. Я говорю, что бывают ситуации, когда это нужно, и лучше бы, если бы формат передачи это позволял.bealex
08.03.2016 15:05+1Мне кажется, что нужно выбирать инструменты, исходя из задачи. Подходит больше JSON — используем JSON, подходит KTV — используем его.
У меня не было цели сделать универсальный формат для всего, поэтому не понимаю, с чем вы спорите. Да, он не подходит для всех задач, мне казалось, что я это чётко постулировал сам.
vintage
08.03.2016 13:33-2bealex
08.03.2016 13:39Tree занятный, спасибо. И простой, это огромный плюс. Но мне хотелось чего-то ближе к привычному JSON'у.
Как бороться с развесистыми структурами — более-менее известно, KTV просто предлагает для этого способ, встроенный в формат.vintage
08.03.2016 13:49Нет ничего ближе к привычному JSON, чем сам JSON. Реализовали бы KTV поверх JSON — сильно бы расширили число адептов и уменьшили сложность реализации.
Речь о том, что развесистые структуры не нужны вообще — только плоские списки фиксированной глубины. Их и дебажить проще, и в реализации они проще, и работают быстрее, и отдельного формата данных не требуют.bealex
08.03.2016 13:58-1Увы, реальность требует разных структур.
KTV — это надстройка над JSON, любой валидный JSON является валидным KTV. Если же зафиксировать какие-то правила JSON'а (как, например, предлагает http://jsonapi.org), то увеличится запись, мы немного приблизимся к XML по структуре (или даже к чему-то вроде XML-RPC), чего тоже хочется избежать.vintage
08.03.2016 14:07+1Например?
А что толку от того, что JSON можно распарсить KTV парсером? JSON парсер и так есть везде. Вот если бы KTV можно было распарсить JSON парсером — это было бы полезно. Так что стремление сделать похоже, но несовместимо — контрпродуктивно, и можно быть смелее в выборе синтаксиса, лучше оптимизировав его под решаемые задачи.bealex
08.03.2016 14:10+1Например?
Самый простой пример — когда у вас нет контроля за самим форматом. Приходится работать с тем, что есть.
JSON парсер и так есть везде.
Это совершенно верно.
(Впрочем, когда был распространен XML, также говорили про сам JSON)
trueClearThinker
08.03.2016 13:46Есть ли бенчмарки скорости сериализации/десериализации в сравнении с JSON/YAML/Protobuf?
bealex
08.03.2016 13:54Пока нет. Мне интересно пока, на правильном ли я пути.
По ощущениям, парсинг должен быть примерно, как JSON по скорости.
mgyk
08.03.2016 14:14+1Посмотрите на MessagePack http://msgpack.org/ сильно быстрее и компактнее JSON. Хотя типы поддерживает тоже только самые простые.
bealex
08.03.2016 14:19Ага, спасибо. Про него знаю. Он хороший, но бинарный, мне нужен именно текстовый формат, завтра опубликую статью про S2, из неё будет немного яснее, зачем.
NeoCode
08.03.2016 14:47Советую с комментариями следать следующее
- Однострочные комментарии — в стиле си-подобных языков, начинаются с // и до конца строки
- Многострочные комментарии — также в стиле си-подобных языков, начинаются и заканчиваются также как в С/С++ с /* и заканчиваются */
- Блочные комментарии — начинаются с #, за которыми следует корректный блок кода (фигурно-скобочная группа { } ). Удобно, когда нужно отключить целый блок и не хочется ставить закрывающий многострочный комментарий, искать куда бы поставить */, а затем удалять его когда блок снова понадобится.
Вот тогда будет полный набор всех возможностей.
bealex
08.03.2016 15:07Третий вариант — очень интересен, спасибо. Не думаю, что буду его реализовывать (нет задачи сделать все-все-все), но как метод отладки структуры — супер!
zagayevskiy
08.03.2016 16:31-14) Выкинуть комментарии вообще, ибо не нужны они в формате передачи данных. Оно не для того, чтобы его читать.
bealex
08.03.2016 16:33+1Формат требуется не только для передачи данных.
zagayevskiy
08.03.2016 18:34Ну расскажите нам тогда, зачем он ещё нужен.
ZurgInq
08.03.2016 19:56+3Документация. Для этих целей даже был придуман (найден) способ комментариев в самом json.
{ "title": "заголовок", "title": "KTV. Новый JSON" }
bealex
08.03.2016 20:00:-) Не знал о таком. Смешной.
ZyXI
08.03.2016 22:15+2Стандарт явно говорит, что поведение парсера в этом случае не определено. Например, мой вариант
json_decode
для Neovim превратит пример в{"_TYPE": v:msgpack_types.map, "_VAL": [["title", "заголовок"], ["title", "KTV. Новый JSON"]]}
. RFC 7159:
An object whose names are all unique is interoperable in the sense
that all software implementations receiving that object will agree on
the name-value mappings. When the names within an object are not
unique, the behavior of software that receives such an object is
unpredictable. Many implementations report the last name/value pair
only. Other implementations report an error or fail to parse the
object, and some implementations report all of the name/value pairs,
including duplicates.
ZyXI
08.03.2016 22:23А json_decode из Vim покажет вам
E721: Duplicate key in Dictionary: "title"
.
Замечу, что мой парсер не просто выдаст такой прикольный словарь, но ещё и будет парсить пример два раза: один до того, как поймёт, что нужно использовать «специальный словарь», другой после: информация о порядке ключей на первом проходе не сохранялась, «особые случаи» намеренно не оптимизировались из?за их предположительной редкости. Разумеется, это дело сопровождается двойным выделением памяти и дополнительным освобождением.
zagayevskiy
08.03.2016 22:10Зачем хранить документацию в JSON?
ZurgInq
08.03.2016 22:31Наоборот. В документации хранится образец в виде json. Да хоть swagger документация тому пример. Только там комментарии в json не используется, т.к.это всё таки трюк.
NeoCode
09.03.2016 10:40Для хранения данных. Различные конфигурационные/настроечные файлы например. JSON и подобные ему форматы это не только web, если что.
SBKarr
08.03.2016 18:49А что, если всё же разделить человекопонятные форматы для данных и конфигурации, и форматы машинного обмена данными? В таком случае, не потребуется долго и мучительно искать замену JSON, в которой будет больше типов/меньше кавычек. У нас, например, JSON используется, если нужно получить человекочитаемые данные, а для машинного обмена — CBOR. Сервер определяет, какой формат отдать на основе заголовка Accept. Выигрыш в скорости разбора почти в два раза по процессору и немного по памяти (не нужно буферизовать строку, если встречен '\'). Выигрыша в объёме данных около 5%.
P.S. Оба декодировщика — потоковые на основе std::basic_stream. Позволяет начать разбирать данные ещё до их полного получения.bealex
08.03.2016 18:53Хорошее решение. Но такое редко необходимо, требует существенно больше поддержки на всех платформах, которые взаимодействуют.
С другой стороны, вы используете, например, JSON, а не XML. Почему? Потому что JSON удобнее. Мне кажется, что KTV будет ещё удобнее.
prefrontalCortex
08.03.2016 21:16Плюсы и минусы JSON
Справедливости ради, в спецификации JSON Schema они таки есть.
Отсутствие ссылок
Это — формат описания объектов в JavaScript'е
По забавному совпадению, JSON-документ с некоторыми купюрами (null/None) является валидным описанием dict в Python. Кстати, именно поэтому ваше ограничение на тип объектов в массиве выглядит надуманным, в питоне, как и в JS, можно класть объекты разного типа в массив.bealex
08.03.2016 21:25Ссылки несложно ввести, при необходимости, я так делал в ASS.
… можно класть объекты разного типа в массив
Это можно делать почти всегда, я не говорю про невозможность. Я говорю про неудобства, с этим связанные.
loststylus
09.03.2016 14:25Если нужно прямо сэкономить на трафике, возьмите protocol buffers ;) Если нет, то есть JSON и YAML, которые по крайней мере везде поддерживаются из коробки
spmbt
09.03.2016 17:38Ради коллекции, упомяну свой велосипед: JSON с комментариями (jsonComm). Сделан на JS, не слишком трудно сделать на других языках, но скорости KTV (из-за кавычек и структуры) не достигнет.
jehy
10.03.2016 11:06+1Ни в коем случае не хочу сказать, что JSON идеален, и всегда для всего подходит. Таких стандартов и форматов просто не бывает. Но конкретно описанные вами проблемы — исключительно проблемы плохого кода.
Кавычки. По стандарту (в качестве стандарта я беру текст отсюда: www.json.org) все названия должны заключаться в кавычки. Если мы используем JSON, например, для передачи данных по сети, то это излишество.
Вы серьёзно? Вы правда считаете, что 0.5% оптимизации объёма чего-то стоят? Может, тогда стоит сэкономить в языках разработки на пробелах, табуляции и точках с запятой, чтобы экономить диск программиста?
Отсутствие типизации. Точнее, типы есть, но всего строка/число/true/false/null. И объекты с массивами. Всё. Ни дат, ни целых/дробных чисел нет. Нет возможности пометить типами объекты, чтобы проще было модель понять.
Типизация указана в модели, которая используется вашим генератором и потребителем. Если типы одинаковые, то сериализируются и десериализируются они одинаково. И совершенно непонятно, зачем вам "понимать" JSON. Разве что если вам привезли тележку неизвестного JSON и сказали его познавать. Но это какая-то странная ситуация.
Отсутствие стандартов записи объектов. Это, например, может привести к массивам со смешанными объектами внутри. Когда приходится такое парсить — становится больно.
А вы это вручную парсите? Не надо так! У меня даже на Arduino стоит библиотека для создания и разборки JSON. И если у вас корректная модель, то никакой боли не возникает.
Отсутствие ссылок. Если в JSON записывать иерархическую структуру объектов, то регулярно встречаются ссылки на один и тот же объект из разных мест. JSON не даёт возможности сослаться на первое использование. Нужно либо что-то придумывать, либо повторять объекты целиком, что плохо сказывается на размере структуры и на сложности парсинга.
Не надо ничего придумывать, в случае повторяющихся объектов надо использовать идентификаторы объектов, которые заменят вам любые ссылки. Всё придумано давно и очень очевидно.
Про комментарии в стандарте для передачи данных даже говорить не буду — и так очевидно.
Зато вы придумали свой стандарт. И если вам придётся работать с кем-то ещё, или другими сервисами — вас все проклянут — никому не хочется разбираться с самописным стандартом.bealex
10.03.2016 11:43Вы правы.
Но, кроме указанных вами кейсов, существуют и другие, в которых решения, которые я применяю — более выгодны, чем применяемые вами. Для этого и существует множество форматов, которые используются в разных ситуациях.
Но вы правы, всё перечисленное мной можно реализовать в JSON. Впрочем, и в простом тексте можно, и в XML, и в ещё многих-многих вариантах.jehy
10.03.2016 11:49Да, жду вашу публикацию про S2 — очень интересно посмотреть, какой будет кейс под такое решение. Возможно, лучше было бы объединить публикации — или выкатить одновременно — тогда было бы понятнее, зачем вам такой формат.
bealex
10.03.2016 11:52Первая часть тут: https://habrahabr.ru/post/278787/
Сейчас немного обкатаю код на Swift и через какое-то время выложу вторую часть, вместе со ссылкой на библиотеки и исходный код.
k12th
Оо, в этом месяце еще не было джейсона без кавычек и с комментариями.
bealex
Велосипеды — это наше всё!
bealex
Не знаете, как бы их найти тут? Я бы почитал про другие попытки, интересно.
kafeman
«Свой JSON» действительно появляется тут чуть ли не каждый месяц. И количество плюсов у комментария выше показывает, что все кроме вас их видят.
Вот то, что я нашел за 5 минут:
https://habrahabr.ru/post/247473/
https://habrahabr.ru/post/269993/
https://habrahabr.ru/post/269461/
https://habrahabr.ru/post/130112/
https://habrahabr.ru/post/248147/
bealex
Спасибо за ссылки!
extempl
Всё-таки есть и обратная сторона — вы, как и я, заинтересовались заголовком и тизером и зашли почитать. Вопрос — чего мы с вами ожидали от статьи? Лулзов? А может, всё-таки, надеемся что кто-то таки придумал более удобный формат? Если второе, то я думаю, попытки имеют право быть — почему нет?
grossws
Я, например, зашел посмотреть на проблему, которая привела к попытке создания своего формата. Если говорить про сериализацию, то ничего принципиально интересного в дополнительной типизации полей я не вижу, т. к. это усложняет десериализатор, но не даёт гарантий, что пришедший объект валиден (например, могут нарушаться какие-нибудь инварианты).
Ну и ради лулзов, ессно.
mayorovp
+: https://habrahabr.ru/post/224261/
kafeman
Да их еще несколько десятков тут найти можно.
mayorovp
Да, но там я заинтересованное лицо :)