Yson

В данном разделе собрана информация про YSON — JSON-подобный формат данных, разработанный в Яндексе.

Примечание

SQL-функции для работы с YSON описаны здесь

Введение

К основным отличиям YSON от JSON относится:

  1. Поддержка бинарного представления скалярных типов (чисел, строк и булевого типа);
  2. Атрибуты: произвольный словарь, который можно установить дополнительно на литерал любого (даже скалярного) типа.

Кроме того существуют синтаксические отличия:

  1. Вместо запятой в качестве разделителя используется точка с запятой;
  2. В словарях ключ от значения отделяется не двоеточием, а знаком равенства: =;
  3. Строковые литералы не обязательно всегда заключать в кавычки (только если иначе возникает неоднозначность при парсинге).

Имеется следующий набор скалярных типов:

  1. Строки (string);
  2. Знаковые и беззнаковые 64-битные целые числа (int64 и uint64 );
  3. Числа с плавающей точкой двойной точности (double);
  4. Булев (логический) тип (boolean);
  5. Специальный тип entity, имеющий всего один литерал (#).

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

Есть два композитных типа:

  1. Список (list);
  2. Словарь (map).

Скалярные типы

Строки

Токены строк бывают трех видов:

  1. Идентификаторы задаются регулярным выражением [A-Za-z_][A-Za-z0-9_.\-]* (Первый символ - буква или нижнее подчеркивание, со второго символа могут быть дополнительно использованы цифры и символы -,.). Идентификатор задает строку с идентичным ему содержимым и используется в первую очередь для краткости (не нужно ставить кавычки).

    Примеры:

    • abc123;
    • _;
    • a-b.
  2. Текстовые строкиC-escaped строки в двойных кавычках.

    Примеры:

    • "abc123";
    • "";
    • "quotation-mark: \", backslash: \\, tab: \t, unicode: \xEA".
  3. Бинарные строки: \x01 + length (protobuf sint32 wire format) + data (<length> bytes).

Знаковые 64-битные целые числа (int64)

Два способа записи:

  1. Текстовый: (0, 123, -123, +123);
  2. Бинарный: \x02 + value (protobuf sint64 wire format).

Беззнаковые 64-битные целые числа (uint64)

Два способа записи:

  1. Текстовый: (10000000000000, 123u);
  2. Бинарный: \x06 + value (protobuf uint64 wire format).

Числа с плавающей точкой (double)

Два способа записи:

  1. Текстовый: (0.0, -1.0, 1e-9, 1.5E+9, 32E1, %inf, %-inf, %nan);
  2. Бинарный: \x03 + protobuf double wire format.

Важно

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

Важно

Значения %inf, %-inf, %nan не существуют в JSON, поэтому вызов Yson::SerializeJson с содержащим эти значения YSON приведет к ошибке

Булевы литералы (boolean)

Два способа записи:

  1. Текстовый (%false, %true);
  2. Бинарный (\x04, \x05).

Entity (entity)

Entity представляет собой атомарное скалярное значение, не имеющее никакого собственного содержимого. Сценарии, в которых данный тип может быть полезен, разнообразны. Например, часто entity обозначает null. При этом entity может иметь аттрибуты

Лексически entity кодируется символом решетки: #.

Выделенные литералы

Специальные токены:

;, =, #, [, ], {, }, <, >, ), /, @, !, +, ^, :, ,, ~.

Не все эти символы используются в YSON, некоторые используются в YPath.

Композитные типы

Список (list)

Задается следующим образом: [value; ...; value], где value — литералы произвольных скалярных или композитных типов.

Пример: [1; "hello"; {a=1; b=2}].

Словарь (map)

Задается следующим образом: {key = value; ...; key = value}. Здесь *key* — литералы строкового типа, а value — литералы произвольных скалярных или композитных типов.

Пример: {a = "hello"; "38 parrots" = [38]}.

Атрибуты

На любой литерал в YSON можно установить атрибуты. Записывается это так: <key = value; ...; key = value> value. Внутри угловых скобок синтаксис аналогичен словарю. Например, <a = 10; b = [7,7,8]>"some-string" или <"44" = 44>44. Но чаще всего атрибуты можно встретить на литералах типа entity, например, <id="aaad6921-b5704588-17990259-7b88bad3">#.

Грамматика

YSON-данные бывают трех типов:

  1. Node (одно дерево, в примере — <tree>)
  2. ListFragment (значения, разделенные ;, в примере — <list-fragment>)
  3. MapFragment (пары ключ-значение, разделенные ;, в примере — <map-fragment>)

Грамматика (определяется с точностью до пробельных символов, которые могут быть в произвольном количестве добавлены и удалены между токенами):

          <tree> = [ <attributes> ], <object>;
        <object> = <scalar> | <map> | <list> | <entity>;

        <scalar> = <string> | <int64> | <uint64> | <double> | <boolean>;
          <list> = "[", <list-fragment>, "]";
           <map> = "{", <map-fragment>, "}";
        <entity> = "#";
    <attributes> = "<", <map-fragment>, ">";

 <list-fragment> = { <list-item>, ";" }, [ <list-item> ];
     <list-item> = <tree>;

  <map-fragment> = { <key-value-pair>, ";" }, [ <key-value-pair> ];
<key-value-pair> = <string>, "=", <tree>;  % Key cannot be empty

Символ ; после последнего элемента внутри <list-fragment> и <map-fragment> может быть опущен. Следующие конструкции следует считать валидными при чтении:

C ; на конце

Сокращенная запись

<a=b;>c
{a=b;}
1;2;3;
<a=b>c
{a=b}
1;2;3

Примеры

  • Map (Node)
{ performance = 1 ; precision = 0.78 ; recall = 0.21 }
  • Map (Node)
{ cv-precision = [ 0.85 ; 0.24 ; 0.71 ; 0.70 ] }
  • List (Node)
[ 1; 2; 3; 4; 5 ]
  • String (Node)
foobar
"hello world"
  • Int64 (Node) 42

  • Double (Node) 3.1415926

  • ListFragment

{ key = a; value = 0 };
{ key = b; value = 1 };
{ key = c; value = 2; unknown_value = [] }
  • MapFragment
do = create; type = table; scheme = {}
  • HomeDirectory (Node)
{ home = { sandello = { mytable = <type = table> # ; anothertable = <type = table> # } ; monster = { } } }

YPATH

В данном разделе собрана информация про YPath — язык, описывающий пути к объектам в YSON.

YPath представляет собой язык описания путей, которые идентифицирует объекты в YSON. Язык позволяет обращаться к узлам и указывать аннотации, которые могут быть полезны при совершении операций над узлами, такими как запись и чтение свойств.

Например:

  • /0-25-3ec012f-406daf5c/@type — путь к атрибуту type объекта с идентификатором 0-25-3ec012f-406daf5c;

Существует несколько разновидностей YPath. В самом простом случае YPath представляет собой строку, кодирующую путь.

Лексика

Строка, кодирующая простой YPath, разбивается на следующие токены:

  1. Специальные символы: прямой слеш (/), "собака" (@), амперсанд (&), звездочка (*);
  2. Литералы: максимальная непустая последовательность неспециальных символов. В литералах разрешен escaping вида \<escape-sequence>, где в качестве <escape-sequence> может выступать один из символов \, /, @, &, *, [, {, а также выражение вида x<hex1><hex2>, где <hex1> и <hex2> — шестнадцатеричные цифры.

Синтаксис и семантика

Структурно YPath имеет вид /<relative-path>. <relative-path> разбирается последовательно слева направо, в результате чего возникают шаги перемещения по дереву следующих видов:

  • Переход к потомку: последовательность из токена / и литерала.
    Данный тип шагов применим к словарям и спискам. В случае словаря литерал должен содержать имя потомка. Пример: /child — переход к потомку с именем child.
    В случае списка литерал должен содержать целое число в десятичной системе счисления — номер потомка. Потомки в списке нумеруются с нуля. Разрешены также отрицательные номера, которые нумеруют потомков с конца списка. Примеры: /1 — переход ко второму потомку в списке, /-1 — переход к последнему потомку в списке;
  • Переход к атрибуту: последовательность из токенов /@ и литерала.
    Данный тип шагов применим в любой точке пути и означает переход к атрибуту с данными именем. Пример: /@attr — переход к атрибуту с именем attr.

Примечание

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

Примеры

$data = Yson(@@{"0-25-3ec012f-406daf5c" = {a=<why="I can just do it">1;b=2}}@@);
SELECT Yson::SerializeJson($data), Yson::SerializeJson(Yson::YPath($data, "/0-25-3ec012f-406daf5c/a/@/why"));

Результат:

column0

column1

{
  "0-25-3ec012f-406daf5c": {
    "a": {
      "$attributes": {
        "why": "I can just do it"
      },
      "$value": 1
    },
    "b": 2
  }
}

"I can just do it"

$data = Yson(@@{
  a = <a=z;x=y>[
    {abc=123; def=456};
    {abc=234; xyz=789; entity0123 = #};
  ];
  b = {str = <it_is_string=%true>"hello"; "38 parrots" = [38]};
  entity0 = <here_you_can_store=something>#;
  }
@@);

SELECT Yson::ConvertToStringDict(Yson::YPath($data, "/a/@")) AS attrs_root,
Yson::SerializeJson(Yson::YPath($data, "/b/str/@")) AS attrs_b_str,
Yson::SerializeJson(Yson::YPath($data, "/b/str/@/it_is_string")) AS attr_exact,
Yson::SerializeJson(Yson::YPath($data, "/a/0")) as array_index0,
Yson::SerializeJson(Yson::YPath($data, "/a/-1")) as array_last,
Yson::SerializeJson(Yson::YPath($data, "/entity0")) as entity,
Yson::SerializeJson(Yson::YPath($data, "/a/#entity0123/abc")) as entity1,
Yson::SerializeJson(Yson::YPath($data, "/a")) AS whole_a,
Yson::SerializeJson($data) AS whole_data;

Результат:

attrs_root

attrs_b_str

attr_exact

array_index0

array_last

entity

entity1

whole_a

whole_data

{
    "a": "z",
    "x": "y"
}
{
    "it_is_string": true
}
true
{
    "abc": 123,
    "def": 456
}
{
    "abc": 234,
    "entity0123": null,
    "xyz": 789
}
{
    "$attributes": {
        "here_you_can_store": "something"
    },
    "$value": null
}
null
{
    "$attributes": {
        "a": "z",
        "x": "y"
    },
    "$value": [
        {
            "abc": 123,
            "def": 456
        },
        {
            "abc": 234,
            "entity0123": null,
            "xyz": 789
        }
    ]
}
{
    "a": {
        "$attributes": {
            "a": "z",
            "x": "y"
        },
        "$value": [
            {
                "abc": 123,
                "def": 456
            },
            {
                "abc": 234,
                "xyz": 789
            }
        ]
    },
    "b": {
        "38 parrots": [
            38
        ],
        "str": {
            "$attributes": {
                "it_is_string": true
            },
            "$value": "hello"
        }
    }
}