Yson
YSON — разработанный в Яндексе формат данных, похожий на JSON.
-
Сходства с JSON:
- не имеет строгой схемы;
- помимо простых типов данных поддерживает словари и списки в произвольных комбинациях.
-
Некоторые отличия от JSON:
- Помимо текстового представления имеет и бинарное;
- В текстовом представлении вместо запятых — точки с запятой, а вместо двоеточий — равно;
-
Поддерживается концепция «атрибутов», то есть именованных свойств, которые могут быть присвоены узлу в дереве.
Особенности реализации и функциональность модуля:
-
Наравне с YSON данный модуль поддерживает и стандартный JSON, что несколько расширяет область его применения.
-
Работает с DOM представлением YSON в памяти, которое в терминах YQL передается между функциями как «ресурс» (см. описание специальных типов данных). Большинство функций модуля имеют семантику запроса на выполнение указанной операции с ресурсом и возвращают пустой optional, если операция не удалась из-за несоответствия фактического типа данных ожидаемому.
-
Предоставляет несколько основных классов функций (полный список и подробное описание функций см. ниже):
Yson::Parse***
— получение ресурса с DOM-объектом из сериализованных данных, все дальнейшие операции выполняются уже над полученным ресурсом;Yson::From
— получение ресурса с DOM-объектом из простых типов данных YQL или контейнеров (списков или словарей);Yson::ConvertTo***
— преобразовать ресурс к простым типам данных или контейнерам;Yson::Lookup***
— получение одного элемента списка или словаря с опциональным преобразованием в нужный тип данных;Yson::YPath***
— получение одного элемента дерева документа по указанному относительному пути с опциональным преобразованием в нужный тип данных;Yson::Serialize***
— получить из ресурса копию его данных, сериализованную в одном из форматов;
-
Для удобства при передаче сериализованного Yson и Json в функции, ожидающие на входе ресурс с DOM-объектом, неявное преобразование через
Yson::Parse
илиYson::ParseJson
происходит автоматически. Также в SQL синтаксисе оператор точки или квадратных скобок автоматически добавляет вызовYson::Lookup
. Для сериализации ресурса по-прежнему нужно вызыватьYson::ConvertTo***
илиYson::Serialize***
. Таким образом, например, получение элемента "foo" словаря из колонки mycolumn типа Yson в виде строки может выглядеть так:SELECT Yson::ConvertToString(mycolumn["foo"]) FROM mytable;
илиSELECT Yson::ConvertToString(mycolumn.foo) FROM mytable;
. В варианте с точкой можно экранировать спецсимволы по общим правилам для индентификаторов.
Функции модуля стоит рассматривать как «кубики», из которых можно собирать разные конструкции, например:
Yson::Parse*** -> Yson::Serialize***
— конвертация из одного формата в другой;Yson::Parse*** -> Yson::Lookup -> Yson::Serialize***
— извлечение значения указанного поддерева в исходном дереве YSON;Yson::Parse*** -> Yson::ConvertToList -> ListMap -> Yson::Lookup***
— извлечение элементов по ключу из YSON списка.
Примеры
$node = Json(@@
{"abc": {"def": 123, "ghi": "привет"}}
@@);
SELECT Yson::SerializeText($node.abc) AS `yson`;
-- {"def"=123;"ghi"="\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82"}
$node = Yson(@@
<a=z;x=y>[
{abc=123; def=456};
{abc=234; xyz=789};
]
@@);
$attrs = Yson::YPath($node, "/@");
SELECT
ListMap(Yson::ConvertToList($node), ($x) -> { return Yson::LookupInt64($x, "abc") }) AS abcs,
Yson::ConvertToStringDict($attrs) AS attrs,
Yson::SerializePretty(Yson::Lookup($node, "7", Yson::Options(false AS Strict))) AS miss;
/*
- abcs: `[123; 234]`
- attrs: `{"a"="z";"x"="y"}`
- miss: `NULL`
*/
Yson::Parse...
Yson::Parse(Yson{Flags:AutoMap}) -> Resource<'Yson2.Node'>
Yson::ParseJson(Json{Flags:AutoMap}) -> Resource<'Yson2.Node'>
Yson::ParseJsonDecodeUtf8(Json{Flags:AutoMap}) -> Resource<'Yson2.Node'>
Yson::Parse(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>? -- принимает YSON в любом формате
Yson::ParseJson(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
Yson::ParseJsonDecodeUtf8(String{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
Результат всех трёх функций является несериализуемым: его можно только передать на вход другой функции из библиотеки Yson, но нельзя сохранить в таблицу или вернуть на клиент в результате операции — попытка так сделать приведет к ошибке типизации. Также запрещено возвращать его за пределы подзапросов: если это требуется, то надо вызвать Yson::Serialize, а оптимизатор уберёт лишнюю сериализию и десериализацию, если материализация в конечном счёте не потребуется.
Примечание
Функция Yson::ParseJsonDecodeUtf8
ожидает, что символы, выходящие за пределы ASCII, должны быть дополнительно заэкранированы.
Yson::From
Yson::From(T) -> Resource<'Yson2.Node'>
Yson::From
является полиморфной функцией, преобразующей в Yson ресурс большинство примитивных типов данных и контейнеров (списки, словари, кортежи, структуры и т.п.). Тип исходного объекта должен быть совместим с Yson. Например, в ключах словарей допустимы только типы String
или Utf8
, а вот String?
или Utf8?
уже нет.
Пример
SELECT Yson::Serialize(Yson::From(TableRow())) FROM table1;
Yson::WithAttributes
Yson::WithAttributes(Resource<'Yson2.Node'>{Flags:AutoMap}, Resource<'Yson2.Node'>{Flags:AutoMap}) -> Resource<'Yson2.Node'>?
Добавляет к узлу Yson (первый аргумент) атрибуты (второй аргумент). Атрибуты должны представлять из себя узел map.
Yson::Equals
Yson::Equals(Resource<'Yson2.Node'>{Flags:AutoMap}, Resource<'Yson2.Node'>{Flags:AutoMap}) -> Bool
Проверка деревьев в памяти на равенство, толерантная к исходному формату сериализации и порядку перечисления ключей в словарях.
Yson::GetHash
Yson::GetHash(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64
Вычисление 64-битного хэша от дерева объектов.
Yson::Is...
Yson::IsEntity(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsString(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsDouble(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsUint64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsInt64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsBool(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Yson::IsDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> bool
Проверка, что текущий узел имеет соответствующий тип. Entity это #
.
Yson::GetLength
Yson::GetLength(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64?
Получение числа элементов в списке или словаре.
Yson::ConvertTo...
Yson::ConvertTo(Resource<'Yson2.Node'>{Flags:AutoMap}, Type<T>) -> T
Yson::ConvertToBool(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Bool?
Yson::ConvertToInt64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Int64?
Yson::ConvertToUint64(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Uint64?
Yson::ConvertToDouble(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Double?
Yson::ConvertToString(Resource<'Yson2.Node'>{Flags:AutoMap}) -> String?
Yson::ConvertToList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Resource<'Yson2.Node'>>
Yson::ConvertToBoolList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Bool>
Yson::ConvertToInt64List(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Int64>
Yson::ConvertToUint64List(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Uint64>
Yson::ConvertToDoubleList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<Double>
Yson::ConvertToStringList(Resource<'Yson2.Node'>{Flags:AutoMap}) -> List<String>
Yson::ConvertToDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Resource<'Yson2.Node'>>
Yson::ConvertToBoolDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Bool>
Yson::ConvertToInt64Dict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Int64>
Yson::ConvertToUint64Dict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Uint64>
Yson::ConvertToDoubleDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Double>
Yson::ConvertToStringDict(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,String>
Важно
Данные функции по умолчанию не делают неявного приведения типов, то есть значение в аргументе должно в точности соответствовать вызываемой функции.
Yson::ConvertTo
является полиморфной функцией, преобразующей Yson ресурс в указанный во втором аргументе тип данных с поддержкой вложенных контейнеров (списки, словари, кортежи, структуры и т.п.).
Пример
$data = Yson(@@{
"name" = "Anya";
"age" = 15u;
"params" = {
"ip" = "95.106.17.32";
"last_time_on_site" = 0.5;
"region" = 213;
"user_agent" = "Mozilla/5.0"
}
}@@);
SELECT Yson::ConvertTo($data,
Struct<
name: String,
age: Uint32,
params: Dict<String,Yson>
>
);
Yson::Contains
Yson::Contains(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
Проверяет наличие ключа в словаре. Если тип объекта map, то ищем среди ключей.
Если тип объекта список, то ключ должен быть десятичным числом - индексом в списке.
Yson::Lookup...
Yson::Lookup(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Resource<'Yson2.Node'>?
Yson::LookupBool(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
Yson::LookupInt64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Int64?
Yson::LookupUint64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Uint64?
Yson::LookupDouble(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Double?
Yson::LookupString(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> String?
Yson::LookupDict(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Dict<String,Resource<'Yson2.Node'>>?
Yson::LookupList(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> List<Resource<'Yson2.Node'>>?
Перечисленные выше функции представляют собой краткую форму записи для типичного сценария использования: Yson::YPath
— переход в словарь на один уровень с последующим извлечением значения — Yson::ConvertTo***
. Второй аргумент для всех перечисленных функций — имя ключа в словаре (в отличие от YPath, без префикса /
) или индекс в списке (например, 7
). Упрощают запрос и дают небольшой выигрыш в скорости работы.
Yson::YPath
Yson::YPath(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Resource<'Yson2.Node'>?
Yson::YPathBool(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Bool?
Yson::YPathInt64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Int64?
Yson::YPathUint64(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Uint64?
Yson::YPathDouble(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Double?
Yson::YPathString(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> String?
Yson::YPathDict(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> Dict<String,Resource<'Yson2.Node'>>?
Yson::YPathList(Resource<'Yson2.Node'>{Flags:AutoMap}, String) -> List<Resource<'Yson2.Node'>>?
Позволяет по входному ресурсу и пути на языке YPath получить ресурс, указывающий на соответствующую пути часть исходного ресурса.
Yson::Attributes
Yson::Attributes(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Dict<String,Resource<'Yson2.Node'>>
Получение всех атрибутов узла в виде словаря.
Yson::Serialize...
Yson::Serialize(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson -- бинарное представление
Yson::SerializeText(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson
Yson::SerializePretty(Resource<'Yson2.Node'>{Flags:AutoMap}) -> Yson -- чтобы увидеть именно текстовый результат, можно обернуть его в ToBytes(...)
Yson::SerializeJson
Yson::SerializeJson(Resource<'Yson2.Node'>{Flags:AutoMap}, [Resource<'Yson2.Options'>?, SkipMapEntity:Bool?, EncodeUtf8:Bool?, WriteNanAsString:Bool?]) -> Json?
SkipMapEntity
отвечает за сериализацию значений в словарях, имеющих значение#
. На значение атрибутов флаг не влияет. По умолчаниюfalse
.EncodeUtf8
отвечает за экранирование символов, выходящих за пределы ASCII. По умолчаниюfalse
.WriteNanAsString
разрешает сериализацию значенийNaN
иInf
в json в виде строк. По умолчаниюfalse
.
Типы данных Yson
и Json
, возвращаемые функциями сериализации, представляет собой частный случай строки, про которую известно, что в ней находятся данные в соответствующем формате (Yson/Json).
Yson::Options
Yson::Options([AutoConvert:Bool?, Strict:Bool?]) -> Resource<'Yson2.Options'>
Передаётся последним опциональным аргументом (который для краткости не указан) в методы Parse...
, ConvertTo...
, Contains
, Lookup...
и YPath...
, которые принимают результат вызова Yson::Options
. По умолчанию все поля Yson::Options
выключены (false), а при включении (true) модифицируют поведение следующим образом:
- AutoConvert — если переданное в Yson значение не в точности соответствует типу данных результата, то значение будет по возможности сконвертировано. Например,
Yson::ConvertToInt64
в этом режиме будет делать Int64 даже из чисел типа Double. - Strict — по умолчанию все функции из библиотеки Yson возвращают ошибку в случае проблем в ходе выполнения запроса (например, попытка парсинга строки не являющейся Yson/Json, или попытка поиска по ключу в скалярном типе, или запрошено преобразование в несовместимый тип данных, и т.п.), а если отключить строгий режим, то вместо ошибки в большинстве случаев будет возвращаться
NULL
. При преобразовании в словарь или список (ConvertTo<Type>Dict
илиConvertTo<Type>List
) плохие элементы будут выброшены из полученной коллекции.
Пример
$yson = @@{y = true; x = 5.5}@@y;
SELECT Yson::LookupBool($yson, "z"); --- null
SELECT Yson::LookupBool($yson, "y"); --- true
SELECT Yson::LookupInt64($yson, "x"); --- Ошибка
SELECT Yson::LookupInt64($yson, "x", Yson::Options(false as Strict)); --- null
SELECT Yson::LookupInt64($yson, "x", Yson::Options(true as AutoConvert)); --- 5
SELECT Yson::ConvertToBoolDict($yson); --- Ошибка
SELECT Yson::ConvertToBoolDict($yson, Yson::Options(false as Strict)); --- { "y": true }
SELECT Yson::ConvertToDoubleDict($yson, Yson::Options(false as Strict)); --- { "x": 5.5 }
Если во всём запросе требуется применять одинаковые значения настроек библиотеки Yson, то удобнее воспользоваться PRAGMA yson.AutoConvert; и/или PRAGMA yson.Strict;. Также эти PRAGMA
являются единственным способом повлиять на неявные вызовы библиотеки Yson, которые возникают при работе с типами данных Yson/Json.