В своём развитии мне пришлось пробежаться по нескольким этапам в нескольких направлениях: Java > Objective C > Swift, Web > Enterprise > Mobile, XML > JSON. Этим путём я шёл на протяжении более 15 лет, подолгу и внимательно задерживаясь на каждом этапе. Нужно идти дальше. За мобильными приложениями можно придумать что-то (наверное, пока не хочется), языков вообще пруд-пруди, ничего интереснее JSON'а не придумали. А зачем его менять?

Дальше я расскажу почему мне не очень нравится 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'а вместо рефлекшна для анализа файлов, необходимость кастомных мапперов (например, для парсинга нестандартных дат). Сам парсер сейчас в процессе активной разработки, и постоянно меняется. Но, если будет достаточный интерес — выложу рабочий проект, обсудим варианты.

Польза от очередного формата данных


image
https://xkcd.com/927/
JSON — так себе формат для передачи данных между приложениями. Это — формат описания объектов в JavaScript'е. А мы постоянно используем его для совершенно других целей, впихивая и по делу и без дела.

Мне кажется, пора переходить к чему-то, более приспособленному к выполняемым задачам. К новому Swift'у с его строгой типизацией, к сложным структурам данных, которые часто приходится передавать на мобильные клиенты с сервера. Нужен формат, синтаксис которого можно будет проверять в IDE, в процессе компиляции и при парсинге. И, мне кажется, что KTV, не меняя кардинально структуру, практически не усложняя уже известный формат, добавляет несколько удобных мелочей.

Выкладываю экспериментальный исходный код с небольшим описанием. Там больше интересны приёмы для парсинга формата на Swift, нежели парсер. Но вдруг кому интересно будет посмотреть: http://github.com/bealex/KTV

Скорее всего, я много чего упустил или не учёл. Может, у кого-то есть похожий опыт? Пишите в комментариях, или в почту: alex@jdnevnik.com, обсудим!

Комментарии (111)


  1. k12th
    08.03.2016 12:07
    +35

    Оо, в этом месяце еще не было джейсона без кавычек и с комментариями.


    1. bealex
      08.03.2016 12:14
      -6

      Велосипеды — это наше всё!


    1. bealex
      08.03.2016 20:01

      Не знаете, как бы их найти тут? Я бы почитал про другие попытки, интересно.


      1. kafeman
        08.03.2016 23:02
        +7

        «Свой 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/


        1. bealex
          08.03.2016 23:14

          Спасибо за ссылки!


        1. extempl
          09.03.2016 11:43
          +2

          Всё-таки есть и обратная сторона — вы, как и я, заинтересовались заголовком и тизером и зашли почитать. Вопрос — чего мы с вами ожидали от статьи? Лулзов? А может, всё-таки, надеемся что кто-то таки придумал более удобный формат? Если второе, то я думаю, попытки имеют право быть — почему нет?


          1. grossws
            09.03.2016 13:17
            +2

            Я, например, зашел посмотреть на проблему, которая привела к попытке создания своего формата. Если говорить про сериализацию, то ничего принципиально интересного в дополнительной типизации полей я не вижу, т. к. это усложняет десериализатор, но не даёт гарантий, что пришедший объект валиден (например, могут нарушаться какие-нибудь инварианты).

            Ну и ради лулзов, ессно.


        1. mayorovp
          09.03.2016 18:05

          1. kafeman
            09.03.2016 18:09

            Да их еще несколько десятков тут найти можно.


            1. mayorovp
              09.03.2016 19:40

              Да, но там я заинтересованное лицо :)


  1. rayz_razko
    08.03.2016 12:11
    +1

    Автор пишет об излишестве кавычек (что ошибочно, ибо JSON позволяет в качестве ключа использовать {'i am a key': 'value'}), но в то же время хочет добавлять типы. Странно как-то.


    1. bealex
      08.03.2016 12:14

      Кавычки в KTV допустимы, как раз для таких случаев.


      1. rayz_razko
        08.03.2016 12:19
        +1

        Если мы используем JSON, например, для передачи данных по сети, то это излишество.

        Я вот об этом. Вас беспокоят лишние байты, но в то же время Вы говорите о том, что нужно передавать типы.


        1. bealex
          08.03.2016 12:25

          Типы передавать необязательно, это я тоже отметил. Также, байтики нужно не только передавать, но и писать.

          Ещё строки не способствуют строгой типизации. Внутри JSON'а — часто объекты, объекты, которые парсятся в строго-типизированные языки. Строки маппить в строготипизированный язык тяжело (посмотрите, сколько парсеров JSON'а существует). Мне кажется, что если зажать этот формат несколько сильнее, то его проще будет редактировать, проще контролировать и работать с ним. Не в любом контексте, а в том, который я указываю.


          1. rayz_razko
            08.03.2016 12:30
            +2

            Если JSON объект парсится в строго типизированном языке, то, скорее всего, существует контракт(модель). Т.е. принимающая сторона ожидает, что в проперти "name" будет лежать строка, а в проперти "age" целочисленный тип данных.


            1. bealex
              08.03.2016 12:38

              Это правда, но часто модель приходится делать уже по существующим JSON'ам (которые делались для сайта, например), поэтому изначально контракты отсутствовали, что приводит к очень, очень сложным моделям для парсинга. Как я отметил в статье, например, разные типы кладутся в коллекции или названия полей недопустимы для целевого языка.

              Опять же, ссылки на объекты часто бывают полезны.


              1. rayz_razko
                08.03.2016 12:42
                +3

                то приводит к очень, очень сложным моделям для парсинга

                Это уже проблема не JSON-а


                1. bealex
                  08.03.2016 12:46
                  -2

                  Конечно, это моя проблема, как разработчика. Про это я и говорю.


                  1. esc
                    08.03.2016 17:54
                    +2

                    А чем поможет ваш формат, если типы все равно не будут указаны (они ж не обязательны) ?


                    1. bealex
                      08.03.2016 18:04
                      -1

                      Если не указывать типы, это просто чуть более короткий JSON, что приятно. Плюс, стандартные типы выведутся из типов литералов.


                      1. zagayevskiy
                        08.03.2016 18:27
                        +4

                        А потом вы будете искать баги в своём парсере? Более короткий JSON? Зачем?


    1. bealex
      08.03.2016 12:16
      +2

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


      1. rayz_razko
        08.03.2016 12:20
        +1

        Вопрос не в хорошей\плохой практике, а в спецификации.


        1. bealex
          08.03.2016 12:25

          Какую спецификацию вы имеете в виду?


          1. 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.


            1. bealex
              08.03.2016 12:46

              А как эта спецификация относится к новому формату? :-)


              1. rayz_razko
                08.03.2016 12:55

                Вы утверждали, что кавычки излишни, я же возразил что это не так. При чем это было написано "между прочим". Суть комментария была совершенно другая.


                1. bealex
                  08.03.2016 13:04

                  :) Ок, понял.


    1. Carduelis
      08.03.2016 14:32
      +1

      Только двойные кавычки, прошу отметить. Иначе — ни один парсер не пропустит.


  1. ainu
    08.03.2016 12:31
    +1

    Это формат удобочитаемый или для передачи данных по сети в большом объёме/с большой скоростью/быстрое сжатие? Потому что с первым проблем нет, а со вторым существующие решения не иделальны.


    1. bealex
      08.03.2016 12:45

      Смотря что подразумевается под большой скоростью и быстрым сжатием. Сам формат для больших структур с повторяющимися объектами должен быть сильно проще и короче, чем JSON (за счёт ссылок, например). Он по-прежнему текстовый, в этом смысле он будет похож на JSON (не как Protocol Buffers, например), и будет сжиматься как обычный текст (типы, например, в случае использования сжатия, почти исчезнут, так как они одинаковые).


      1. Sannis
        11.03.2016 03:36

        Ничто не мешает использовать protobuf для описания формата и при этом сериализовывать данные в JSON.


        1. bealex
          11.03.2016 09:37

          Ммм. Занятный вариант, не думал пока про такое. Спасибо за идею!


  1. aikixd
    08.03.2016 12:56
    +5

    Передача строк без кавычек бред. Каждый раз придется проверять на ключевые слова, раз. Два, это заряженый пистолет нацеленый в ногу. Рано или поздно вы просмотрите код запроса сами.

    object: { a: ..., b: ...}
    object2(+object): { c: ..., d: ... }

    Вот это зачем? И как программа должна догадываться когда это использовать? И что делать парсеру если вы поменяли свойства местами?

    property: value
    another: @property

    Как ссылаться на объекты ниже, выше по иерерхии или в другой ветке?

    Типы указывать вообще не нужно. Для всех данных есть модель которая их описывает. Если модели нет, то программе не с чем работать.

    Посмотрите вот это.


    1. bealex
      08.03.2016 13:08
      -1

      Передача строк без кавычек бред.

      Я так не считаю. Плюс, я не говорю, что кавычки не нужны, я говорю, что они обычно не нужны в именах пропертей.

      И что делать парсеру если вы поменяли свойства местами?

      Резолвить это, как и ссылки, после чтения файла.

      Как ссылаться на объекты ниже, выше по иерерхии или в другой ветке?

      Сейчас у меня ссылки идут от корня структуры, это решает все проблемы.

      Типы указывать вообще не нужно. Для всех данных есть модель которая их описывает.

      Это не всегда так, плюс, иногда бывает удобно саму модель строить по этому файлу (про это я выложу статью на днях). В этом случае типы — необходимая вещь. Если же есть модель, то типы можно не указывать, как и в JSON.


      1. 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" }
        ]

        И тут мне не нужен тип.


        1. bealex
          08.03.2016 13:48

          Если уж хочется без свойств то лучше как-то так:

          Да, использование ID — один из способов. Но их сложно поддерживать (это не важно при автогенерации, важно при ручном написании).

          И тут мне не нужен тип.

          Да, я просто предлагаю внутри формата поддержку такого рода данных (если требуется). Завтра я опубликую статью про S2, станет чуть понятнее, как именно удобно этим пользоваться.

          Во первых, как я узнаю тип корня?...

          Это не та задача, для которой введены (повторюсь, необязательные) типы.

          Но и тут они могут помочь. Например, приходил объект с типом A, а потом кто-то на сервере решил, что нужен в этом месте тип Б. Принимающая сторона может проверить тип и выдать сообщение об ошибке, вместо попытки впихнуть объект Б внутрь А.


          1. zagayevskiy
            08.03.2016 16:23

            Зачем вы данные руками пишете?


            1. vintage
              08.03.2016 16:32

              А почему HTTP протокол текстовый?


              1. zagayevskiy
                08.03.2016 18:31

                Ну, наверное, потому, что в те времена, когда его придумывали, о контенте в сети думали как о гипертексте (hypertext transfer protocol). Ну, и с тех пор появилось мнение о том, что это неправильно. HTTP 2 — бинарный. А вы зачем спрашиваете? Тоже JSON руками пишете?


                1. vintage
                  08.03.2016 20:38
                  -2

                  Будь HTTP изначально бинарным, то страницы мы сейчас открывали бы не по HTTPS, а по SFTP ;-) Сложное это дело, заниматься отладкой по бинарному протоколу.


            1. bealex
              08.03.2016 16:33

              Завтра будет статья про S2, там будет немного понятнее. Но вообще вопрос не совсем понятен. Что значит «зачем»? Иногда требуется для решения поставленной задачи. :)


              1. zagayevskiy
                08.03.2016 18:33

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


                1. bealex
                  08.03.2016 18:49

                  :) Я старался не акцентировать внимание исключительно на передаче данных. Если вдруг где-то пропустил, покажите, пожалуйста. Передача данных — важная, но не единственная задача, решаемая такого рода форматами.


    1. bealex
      08.03.2016 13:29
      +1

      http://jsonapi.org/ знаю. Проект правильный, будет здорово, если им (или аналогичным, не важно каким, на самом деле) будут пользоваться.


  1. corristo
    08.03.2016 12:56

    Почему не искаробочный plist?


    1. bealex
      08.03.2016 13:08

      Он далеко не всегда удобен. Плюс, его совсем неудобно генерировать на стороне сервера.


      1. corristo
        08.03.2016 13:09

        Уверен, для популярных веб-фреймворков найдется готовый плагин plist-сериализатора.


        1. bealex
          08.03.2016 13:31

          Это правда, но его неудобство не только в отсутствии плагинов. Это XML (или вообще бинарь), что усложняет чтение и запись, удлинняет (в случае текста) формат. Он крайне ограничен по типам, и, в отличие от базового XML — не допускает расширения. Даже вложенные объекты им не записать.

          Я пробовал его использовать в разных ситуациях и обычно это оказывалось неудобно.


  1. lair
    08.03.2016 12:58
    +6

    JSON — так себе формат для передачи данных между приложениями

    Почему?

    По стандарту [...] все названия должны заключаться в кавычки. Если мы используем JSON, например, для передачи данных по сети, то это излишество.

    Почему это излишество? Это всего лишь более жесткое требование, упрощающее написание парсера.

    Нет возможности пометить типами объекты, чтобы проще было модель понять.

    А зачем аннотировать типы данных внутри формата передачи данных? Описывайте их в модели.

    Отсутствие стандартов записи объектов.

    Это банально неправда. JSON — и есть такой стандарт.

    Это, например, может привести к массивам со смешанными объектами внутри.

    Это позволяет массивы с разнотипными объектами. Это разве плохо?

    Отсутствие ссылок.

    Добавите ссылки — получите необходимость обработки циклов. Оно вам надо?


    1. bealex
      08.03.2016 13:11
      -2

      Это банально неправда. JSON — и есть такой стандарт.

      Он описывает то, как объекты записываются в JavaScript'е. В других языках, обычно, они описываются совершенно иначе. JSON это не учитывает никак.

      Я не рассматриваю использование KTV для JavaScript'а, нужно это сразу было отметить, моя ошибка.

      Это позволяет массивы с разнотипными объектами. Это разве плохо?

      Да.

      Добавите ссылки — получите необходимость обработки циклов. Оно вам надо?

      А без ссылок — структуры разрастаются, порой, в несколько раз. Но есть и достоинства в обоих решениях.


      1. lair
        08.03.2016 13:14
        -1

        Он описывает то, как объекты записываются в JavaScript'е.

        Уже давно нет.

        В других языках, обычно, они описываются совершенно иначе.

        А кого это волнует? Язык передачи данных не должен учитывать, как описываются объекты на любой стороне, он должен быть совместимым с этими сторонами.

        Это позволяет массивы с разнотипными объектами. Это разве плохо?

        Да.
        Ну то есть возможность написать "у меня есть: корабль, машина, квартира и восемнадцать тонн бриллиантов" — это плохо?


        А без ссылок — структуры разрастаются, порой, в несколько раз.

        А как же компрессия, про которую вы же выше писали?


        1. bealex
          08.03.2016 13:21

          Уже давно нет.

          :) В других языках в названиях полей класса допустимы пробелы? Отсутствуют типы кроме строк, чисел, булевого и null? Ой ли.

          А кого это волнует? Язык передачи данных не должен учитывать, как описываются объекты на любой стороне, он должен быть совместимым с этими сторонами.

          Ну, давайте вернемся к XML, он тоже всё описывает.

          Ну то есть возможность написать «у меня есть: корабль, машина, квартира и восемнадцать тонн бриллиантов» — это плохо?

          Абстрактно — хорошо. В реальности использовать такие структуры очень сложно.

          А как же компрессия, про которую вы же выше писали?

          Вы правы, при передаче данных это менее важно, зип такое сожмет отлично. Но есть и другие моменты.

          При парсинге, в случае использования ссылок, можно использовать один и тот же объект в двух (двадцати) местах. Если же вы дублируете объекты в JSON'е, то и в памяти результирующие объекты будут разные. Это плохо.


          1. lair
            08.03.2016 13:28
            +2

            В других языках в названиях полей класса допустимы пробелы?

            В некоторых — да. Но, что важнее, то, как поля названы в передаваемой структуре, не обязано совпадать с тем, как поля названы на принимающей (или передающей) стороне. В некоторых языках, прямо скажем, и полей с классами может не быть, и что?

            Отсутствуют типы кроме строк, чисел, булевого и null?

            Важно, чтобы были строки, числа и далее по тексту. А то если пытаться охватить в нотации все возможные типы, вы никогда не закончите.

            Ну, давайте вернемся к XML, он тоже всё описывает.

            Во-первых, ваш логический скачок мне непонятен. XML как раз находится на другой стороне спектра — он очень сильно самодостаточен, в то время как JSON требует контекстной интерпретации.

            Ну а во-вторых, XML, кроме избыточности и сложности парсинга ничем не плох, в нем как раз решается половина того, что вас не устраивает.

            В реальности использовать такие структуры очень сложно.

            Что сложного?

            Если же вы дублируете объекты в JSON'е, то и в памяти результирующие объекты будут разные. Это плохо.

            Смотря для чего. Ну и да, повторюсь, ссылки — это круто, но как только вы их добавляете, вы получаете необходимость подсчета циклов (а это само по себе мило) и придумывание правил для вывода этой красоты из исходного графа в дерево.


            1. bealex
              08.03.2016 13:36

              … Важно, чтобы были строки, числа и далее по тексту. А то если пытаться охватить в нотации все возможные типы, вы никогда не закончите.

              Это правда. Но я и не пытаюсь это сделать.

              Я завтра опубликую статью про S2, где будет понятно, зачем я составляю такую структуру.

              Что сложного?

              Сложность в интерпретации и контроле за такими объектами. В Swift (я пишу именно в контексте этого языка) строгая типизация, коллекции — однотипные. Такие структуры тяжело туда впихнуть.

              Далее, сложность в использовании. Требуется обрабатывать массивы (мы сейчас про массивы, не про объекты, где типы у разных полей — разные) специальными средствами, простое итерирование не подходит. Нужно не забыть, что вдруг может попасться другой объект.

              Также такое поведение иногда провоцирует на откровенно неправильные структуры, например, когда массивы используются вместо объектов (по первому индексу — ID, потом имя, потом фамилия...).


              1. lair
                08.03.2016 13:46

                Я завтра опубликую статью про S2, где будет понятно, зачем я составляю такую структуру.

                Ну то есть вы, на самом деле, пишете язык под конкретную задачу, но при этом вам не нравится JSON как формат передачи данных.

                В Swift (я пишу именно в контексте этого языка) строгая типизация, коллекции — однотипные.

                А я пишу на языках со строгой типизацией, где коллекции не однотипны (точнее, полиморфны). Зачем мне ваше искусственное ограничение?

                Требуется обрабатывать массивы (мы сейчас про массивы, не про объекты, где типы у разных полей — разные) специальными средствами, простое итерирование не подходит.

                Почему не подходит? Идете по коллекции итератором, дальше для каждого объекта принимаете решение. Вплоть до динамической диспетчеризации.


                1. bealex
                  08.03.2016 13:52
                  +1

                  Ну то есть вы, на самом деле, пишете язык под конкретную задачу, но при этом вам не нравится JSON как формат передачи данных.

                  Я дополнил текст статьи. Как я уже сказал, я не рассматриваю KTV, как замену JSON. И не хочу, чтобы его использовали при работе с JavaScript'ом, это было бы глупо. Но я считаю, что JSON — не самый хороший вариант описания структур данных, в том числе и при их передаче.

                  Зачем мне ваше искусственное ограничение?

                  Видимо, вам оно не требуется. :-)

                  Почему не подходит?

                  Потому что в общем случае объекты в таком массиве могут быть любые. Динамическая диспетчеризация — это хорошо, но что, если с сервера в массиве начнет приходить тип, который не обрабатывается на клиенте? Появляется обработка ошибок, возможность сломаться в любой момент. Модель данных, наоборот, обычно старается зафиксировать формат передачи. А вы предлагаете его отпустить в свободное изменение и написать принимающей стороне хитрую динамическую логику. Не спорю, такие задачи тоже существуют, но их крайне мало и да, для таких задач JSON подойдёт лучше.


                  1. lair
                    08.03.2016 14:39
                    +2

                    Но я считаю, что JSON — не самый хороший вариант описания структур данных, в том числе и при их передаче.

                    Но так и не указали на какой-нибудь "фундаментальный недостаток".

                    Динамическая диспетчеризация — это хорошо, но что, если с сервера в массиве начнет приходить тип, который не обрабатывается на клиенте?

                    А так будет рано или поздно, поэтому на эту ситуацию все равно надо заложиться.

                    Появляется обработка ошибок, возможность сломаться в любой момент.

                    А так вы обработку ошибок не пишете?

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

                    И что делать, когда переданные данные не соответствуют этому формату?

                    А вы предлагаете его отпустить в свободное изменение и написать принимающей стороне хитрую динамическую логику.

                    Нет, не предлагаю. Я говорю, что бывают ситуации, когда это нужно, и лучше бы, если бы формат передачи это позволял.


                    1. bealex
                      08.03.2016 15:05
                      +1

                      Мне кажется, что нужно выбирать инструменты, исходя из задачи. Подходит больше JSON — используем JSON, подходит KTV — используем его.

                      У меня не было цели сделать универсальный формат для всего, поэтому не понимаю, с чем вы спорите. Да, он не подходит для всех задач, мне казалось, что я это чётко постулировал сам.


                      1. lair
                        08.03.2016 15:26

                        поэтому не понимаю, с чем вы спорите

                        Я спорю с тезисом "JSON — так себе формат для передачи данных между приложениями".


                        1. bealex
                          08.03.2016 15:33

                          Ага, понял.


  1. gearbox
    08.03.2016 13:06
    +3

    TOML. Внизу страницы по ссылке список проектов его использующих и набор библиотек для разных языков.


    1. bealex
      08.03.2016 13:13

      Класс, спасибо, очень интересно. Мне хотелось быть ближе к привычному JSON'у, но формат очень интересный.


  1. vintage
    08.03.2016 13:33
    -2

    1. bealex
      08.03.2016 13:39

      Tree занятный, спасибо. И простой, это огромный плюс. Но мне хотелось чего-то ближе к привычному JSON'у.

      Как бороться с развесистыми структурами — более-менее известно, KTV просто предлагает для этого способ, встроенный в формат.


      1. vintage
        08.03.2016 13:49

        Нет ничего ближе к привычному JSON, чем сам JSON. Реализовали бы KTV поверх JSON — сильно бы расширили число адептов и уменьшили сложность реализации.

        Речь о том, что развесистые структуры не нужны вообще — только плоские списки фиксированной глубины. Их и дебажить проще, и в реализации они проще, и работают быстрее, и отдельного формата данных не требуют.


        1. bealex
          08.03.2016 13:58
          -1

          Увы, реальность требует разных структур.

          KTV — это надстройка над JSON, любой валидный JSON является валидным KTV. Если же зафиксировать какие-то правила JSON'а (как, например, предлагает http://jsonapi.org), то увеличится запись, мы немного приблизимся к XML по структуре (или даже к чему-то вроде XML-RPC), чего тоже хочется избежать.


          1. vintage
            08.03.2016 14:07
            +1

            Например?

            А что толку от того, что JSON можно распарсить KTV парсером? JSON парсер и так есть везде. Вот если бы KTV можно было распарсить JSON парсером — это было бы полезно. Так что стремление сделать похоже, но несовместимо — контрпродуктивно, и можно быть смелее в выборе синтаксиса, лучше оптимизировав его под решаемые задачи.


            1. bealex
              08.03.2016 14:10
              +1

              Например?

              Самый простой пример — когда у вас нет контроля за самим форматом. Приходится работать с тем, что есть.

              JSON парсер и так есть везде.

              Это совершенно верно.

              (Впрочем, когда был распространен XML, также говорили про сам JSON)


              1. vintage
                08.03.2016 14:32

                Изобретение своего формата тут мало чем поможет.

                При этом JSON не стали делать совместимым с XML.


                1. bealex
                  08.03.2016 14:37

                  При этом JSON не стали делать совместимым с XML.

                  И это замечательно.


  1. trueClearThinker
    08.03.2016 13:46

    Есть ли бенчмарки скорости сериализации/десериализации в сравнении с JSON/YAML/Protobuf?


    1. bealex
      08.03.2016 13:54

      Пока нет. Мне интересно пока, на правильном ли я пути.

      По ощущениям, парсинг должен быть примерно, как JSON по скорости.


  1. mgyk
    08.03.2016 14:14
    +1

    Посмотрите на MessagePack http://msgpack.org/ сильно быстрее и компактнее JSON. Хотя типы поддерживает тоже только самые простые.


    1. bealex
      08.03.2016 14:19

      Ага, спасибо. Про него знаю. Он хороший, но бинарный, мне нужен именно текстовый формат, завтра опубликую статью про S2, из неё будет немного яснее, зачем.


    1. GamePad64
      08.03.2016 16:45

      Лучше уж CBOR. Тоже бинарный, но это как-никак RFC, да и возможностей больше.


  1. NeoCode
    08.03.2016 14:47

    Советую с комментариями следать следующее

    1. Однострочные комментарии — в стиле си-подобных языков, начинаются с // и до конца строки
    2. Многострочные комментарии — также в стиле си-подобных языков, начинаются и заканчиваются также как в С/С++ с /* и заканчиваются */
    3. Блочные комментарии — начинаются с #, за которыми следует корректный блок кода (фигурно-скобочная группа { } ). Удобно, когда нужно отключить целый блок и не хочется ставить закрывающий многострочный комментарий, искать куда бы поставить */, а затем удалять его когда блок снова понадобится.
      Вот тогда будет полный набор всех возможностей.


    1. bealex
      08.03.2016 15:07

      Третий вариант — очень интересен, спасибо. Не думаю, что буду его реализовывать (нет задачи сделать все-все-все), но как метод отладки структуры — супер!


    1. zagayevskiy
      08.03.2016 16:31
      -1

      4) Выкинуть комментарии вообще, ибо не нужны они в формате передачи данных. Оно не для того, чтобы его читать.


      1. bealex
        08.03.2016 16:33
        +1

        Формат требуется не только для передачи данных.


        1. zagayevskiy
          08.03.2016 18:34

          Ну расскажите нам тогда, зачем он ещё нужен.


          1. bealex
            08.03.2016 18:49

            Завтра опубликую про S2.


          1. ZurgInq
            08.03.2016 19:56
            +3

            Документация. Для этих целей даже был придуман (найден) способ комментариев в самом json.

            {
              "title": "заголовок",
              "title": "KTV. Новый JSON"
            }


            1. bealex
              08.03.2016 20:00

              :-) Не знал о таком. Смешной.


              1. 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.


                1. ZyXI
                  08.03.2016 22:23

                  А json_decode из Vim покажет вам E721: Duplicate key in Dictionary: "title".

                  Замечу, что мой парсер не просто выдаст такой прикольный словарь, но ещё и будет парсить пример два раза: один до того, как поймёт, что нужно использовать «специальный словарь», другой после: информация о порядке ключей на первом проходе не сохранялась, «особые случаи» намеренно не оптимизировались из?за их предположительной редкости. Разумеется, это дело сопровождается двойным выделением памяти и дополнительным освобождением.


            1. zagayevskiy
              08.03.2016 22:10

              Зачем хранить документацию в JSON?


              1. ZurgInq
                08.03.2016 22:31

                Наоборот. В документации хранится образец в виде json. Да хоть swagger документация тому пример. Только там комментарии в json не используется, т.к.это всё таки трюк.


          1. NeoCode
            09.03.2016 10:40

            Для хранения данных. Различные конфигурационные/настроечные файлы например. JSON и подобные ему форматы это не только web, если что.


  1. lampa
    08.03.2016 17:14

    Мне нравится, когда текст в одну строчку *_*

    {name(string): Alexcoolness(double): 3.1415isAProgrammer(bool): true}


    1. bealex
      08.03.2016 17:41

      Можно разделять запятой или точкой с запятой, если в одну строчку


      1. lampa
        08.03.2016 17:43

        а если в тексте эти символы есть, их экранировать или как?


        1. bealex
          08.03.2016 17:43

          Текст в этом случае нужно заключить в кавычки. Экран стандартный: «\», после него символ считается частью строки.


          1. lampa
            08.03.2016 17:47

            Я еще очень надеюсь, что вы не добавите вместо кавычек отступы. Прям как в jade. А так задумка вполне нравится)


            1. bealex
              08.03.2016 18:03

              Спасибо!

              Нет, на отступы рассчитывать не планируется.


  1. scrutari
    08.03.2016 18:18
    +1

    зануда-моде=on
    Хотелось бы, чтобы все подобные статьи начинались с описания схемы (как XSD для XML).
    зануда-моде=off


    1. bealex
      08.03.2016 18:46

      Всё так.

      Я соберу отзывы, после чего напишу грамматику языка.


  1. amarao
    08.03.2016 18:39
    +7

    ссылки убивают любые поточные процессоры. Плохо.


    1. bealex
      08.03.2016 18:47

      Это правда. Но JSON и так обычно парсится в ассоциативный массив, целиком. В редких случаях, когда нужна поточная обработка — можно ссылки запретить (или вообще не использовать KTV).


  1. SBKarr
    08.03.2016 18:49

    А что, если всё же разделить человекопонятные форматы для данных и конфигурации, и форматы машинного обмена данными? В таком случае, не потребуется долго и мучительно искать замену JSON, в которой будет больше типов/меньше кавычек. У нас, например, JSON используется, если нужно получить человекочитаемые данные, а для машинного обмена — CBOR. Сервер определяет, какой формат отдать на основе заголовка Accept. Выигрыш в скорости разбора почти в два раза по процессору и немного по памяти (не нужно буферизовать строку, если встречен '\'). Выигрыша в объёме данных около 5%.

    P.S. Оба декодировщика — потоковые на основе std::basic_stream. Позволяет начать разбирать данные ещё до их полного получения.


    1. bealex
      08.03.2016 18:53

      Хорошее решение. Но такое редко необходимо, требует существенно больше поддержки на всех платформах, которые взаимодействуют.

      С другой стороны, вы используете, например, JSON, а не XML. Почему? Потому что JSON удобнее. Мне кажется, что KTV будет ещё удобнее.


  1. prefrontalCortex
    08.03.2016 21:16

    Плюсы и минусы JSON
    Отсутствие ссылок
    Справедливости ради, в спецификации JSON Schema они таки есть.

    Это — формат описания объектов в JavaScript'е
    По забавному совпадению, JSON-документ с некоторыми купюрами (null/None) является валидным описанием dict в Python. Кстати, именно поэтому ваше ограничение на тип объектов в массиве выглядит надуманным, в питоне, как и в JS, можно класть объекты разного типа в массив.


    1. bealex
      08.03.2016 21:25

      Ссылки несложно ввести, при необходимости, я так делал в ASS.

      … можно класть объекты разного типа в массив

      Это можно делать почти всегда, я не говорю про невозможность. Я говорю про неудобства, с этим связанные.


  1. loststylus
    09.03.2016 14:25

    Если нужно прямо сэкономить на трафике, возьмите protocol buffers ;) Если нет, то есть JSON и YAML, которые по крайней мере везде поддерживаются из коробки


  1. spmbt
    09.03.2016 17:38

    Ради коллекции, упомяну свой велосипед: JSON с комментариями (jsonComm). Сделан на JS, не слишком трудно сделать на других языках, но скорости KTV (из-за кавычек и структуры) не достигнет.


    1. bealex
      09.03.2016 19:17

      Ага, спасибо, погляжу.


  1. grossws
    09.03.2016 19:23
    +1

    Ну и для конфигов кроме вышеупомянутого toml ещё порекомендую HOCON


  1. jehy
    10.03.2016 11:06
    +1

    Ни в коем случае не хочу сказать, что JSON идеален, и всегда для всего подходит. Таких стандартов и форматов просто не бывает. Но конкретно описанные вами проблемы — исключительно проблемы плохого кода.

    Кавычки. По стандарту (в качестве стандарта я беру текст отсюда: www.json.org) все названия должны заключаться в кавычки. Если мы используем JSON, например, для передачи данных по сети, то это излишество.

    Вы серьёзно? Вы правда считаете, что 0.5% оптимизации объёма чего-то стоят? Может, тогда стоит сэкономить в языках разработки на пробелах, табуляции и точках с запятой, чтобы экономить диск программиста?

    Отсутствие типизации. Точнее, типы есть, но всего строка/число/true/false/null. И объекты с массивами. Всё. Ни дат, ни целых/дробных чисел нет. Нет возможности пометить типами объекты, чтобы проще было модель понять.

    Типизация указана в модели, которая используется вашим генератором и потребителем. Если типы одинаковые, то сериализируются и десериализируются они одинаково. И совершенно непонятно, зачем вам "понимать" JSON. Разве что если вам привезли тележку неизвестного JSON и сказали его познавать. Но это какая-то странная ситуация.

    Отсутствие стандартов записи объектов. Это, например, может привести к массивам со смешанными объектами внутри. Когда приходится такое парсить — становится больно.

    А вы это вручную парсите? Не надо так! У меня даже на Arduino стоит библиотека для создания и разборки JSON. И если у вас корректная модель, то никакой боли не возникает.

    Отсутствие ссылок. Если в JSON записывать иерархическую структуру объектов, то регулярно встречаются ссылки на один и тот же объект из разных мест. JSON не даёт возможности сослаться на первое использование. Нужно либо что-то придумывать, либо повторять объекты целиком, что плохо сказывается на размере структуры и на сложности парсинга.

    Не надо ничего придумывать, в случае повторяющихся объектов надо использовать идентификаторы объектов, которые заменят вам любые ссылки. Всё придумано давно и очень очевидно.

    Про комментарии в стандарте для передачи данных даже говорить не буду — и так очевидно.

    Зато вы придумали свой стандарт. И если вам придётся работать с кем-то ещё, или другими сервисами — вас все проклянут — никому не хочется разбираться с самописным стандартом.


    1. bealex
      10.03.2016 11:43

      Вы правы.

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

      Но вы правы, всё перечисленное мной можно реализовать в JSON. Впрочем, и в простом тексте можно, и в XML, и в ещё многих-многих вариантах.


      1. jehy
        10.03.2016 11:49

        Да, жду вашу публикацию про S2 — очень интересно посмотреть, какой будет кейс под такое решение. Возможно, лучше было бы объединить публикации — или выкатить одновременно — тогда было бы понятнее, зачем вам такой формат.


        1. bealex
          10.03.2016 11:52

          Первая часть тут: https://habrahabr.ru/post/278787/

          Сейчас немного обкатаю код на Swift и через какое-то время выложу вторую часть, вместе со ссылкой на библиотеки и исходный код.