Функции для работы с JSON
В Яндекс.Метрике пользователями передаётся JSON в качестве параметров визитов. Для работы с таким JSON-ом, реализованы некоторые функции. (Хотя в большинстве случаев, JSON-ы дополнительно обрабатываются заранее, и полученные значения кладутся в отдельные столбцы в уже обработанном виде.) Все эти функции исходят из сильных допущений о том, каким может быть JSON, и при этом стараются почти ничего не делать.
Делаются следующие допущения:
- Имя поля (аргумент функции) должно быть константой;
- Считается, что имя поля в JSON-е закодировано некоторым каноническим образом. Например,
visitParamHas('{"abc":"def"}', 'abc') = 1
, ноvisitParamHas('{"\\u0061\\u0062\\u0063":"def"}', 'abc') = 0
- Поля ищутся на любом уровне вложенности, без разбора. Если есть несколько подходящих полей - берётся первое.
- В JSON-е нет пробельных символов вне строковых литералов.
visitParamHas(params, name)
Проверяет наличие поля с именем name
.
Синоним: simpleJSONHas
.
visitParamExtractUInt(params, name)
Пытается выделить число типа UInt64 из значения поля с именем name
. Если поле строковое, пытается выделить число из начала строки. Если такого поля нет, или если оно есть, но содержит не число, то возвращает 0.
Синоним: simpleJSONExtractUInt
.
visitParamExtractInt(params, name)
Аналогично для Int64.
Синоним: simpleJSONExtractInt
.
visitParamExtractFloat(params, name)
Аналогично для Float64.
Синоним: simpleJSONExtractFloat
.
visitParamExtractBool(params, name)
Пытается выделить значение true/false. Результат — UInt8.
Синоним: simpleJSONExtractBool
.
visitParamExtractRaw(params, name)
Возвращает значение поля, включая разделители.
Синоним: simpleJSONExtractRaw
.
Примеры:
visitParamExtractRaw('{"abc":"\\n\\u0000"}', 'abc') = '"\\n\\u0000"';
visitParamExtractRaw('{"abc":{"def":[1,2,3]}}', 'abc') = '{"def":[1,2,3]}';
visitParamExtractString(params, name)
Разбирает строку в двойных кавычках. У значения убирается экранирование. Если убрать экранированные символы не удалось, то возвращается пустая строка.
Синоним: simpleJSONExtractString
.
Примеры:
visitParamExtractString('{"abc":"\\n\\u0000"}', 'abc') = '\n\0';
visitParamExtractString('{"abc":"\\u263a"}', 'abc') = '☺';
visitParamExtractString('{"abc":"\\u263"}', 'abc') = '';
visitParamExtractString('{"abc":"hello}', 'abc') = '';
На данный момент не поддерживаются записанные в формате \uXXXX\uYYYY
кодовые точки не из basic multilingual plane (они переводятся не в UTF-8, а в CESU-8).
Следующие функции используют simdjson, который разработан под более сложные требования для разбора JSON. Упомянутое выше допущение 2 по-прежнему применимо.
isValidJSON(json)
Проверяет, является ли переданная строка валидным json значением.
Примеры:
SELECT isValidJSON('{"a": "hello", "b": [-100, 200.0, 300]}') = 1
SELECT isValidJSON('not a json') = 0
JSONHas(json[, indices_or_keys]…)
Если значение существует в документе JSON, то возвращается 1
.
Если значение не существует, то возвращается 0
.
Примеры:
SELECT JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 1
SELECT JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 4) = 0
indices_or_keys
— это список из нуля или более аргументов каждый из них может быть либо строкой либо целым числом.
- Строка — это доступ к объекту по ключу.
- Положительное целое число — это доступ к n-му члену/ключу с начала.
- Отрицательное целое число — это доступ к n-му члену/ключу с конца.
Адресация элементов по индексу начинается с 1, следовательно элемент 0 не существует.
Вы можете использовать целые числа, чтобы адресовать как массивы JSON, так и JSON-объекты.
Примеры:
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 1) = 'a'
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 2) = 'b'
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -1) = 'b'
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -2) = 'a'
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 1) = 'hello'
JSONLength(json[, indices_or_keys]…)
Возвращает длину массива JSON или объекта JSON.
Если значение не существует или имеет неверный тип, то возвращается 0
.
Примеры:
SELECT JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 3
SELECT JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}') = 2
JSONType(json[, indices_or_keys]…)
Возвращает тип значения JSON.
Если значение не существует, то возвращается Null
.
Примеры:
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}') = 'Object'
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}', 'a') = 'String'
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 'Array'
JSONExtractUInt(json[, indices_or_keys]…)
JSONExtractInt(json[, indices_or_keys]…)
JSONExtractFloat(json[, indices_or_keys]…)
JSONExtractBool(json[, indices_or_keys]…)
Парсит JSON и извлекает значение. Эти функции аналогичны функциям visitParam
.
Если значение не существует или имеет неверный тип, то возвращается 0
.
Примеры:
SELECT JSONExtractInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1) = -100
SELECT JSONExtractFloat('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 2) = 200.0
SELECT JSONExtractUInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', -1) = 300
JSONExtractString(json[, indices_or_keys]…)
Парсит JSON и извлекает строку. Эта функция аналогична функции visitParamExtractString
.
Если значение не существует или имеет неверный тип, то возвращается пустая строка.
У значения убирается экранирование. Если убрать экранированные символы не удалось, то возвращается пустая строка.
Примеры:
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 'a') = 'hello'
SELECT JSONExtractString('{"abc":"\\n\\u0000"}', 'abc') = '\n\0'
SELECT JSONExtractString('{"abc":"\\u263a"}', 'abc') = '☺'
SELECT JSONExtractString('{"abc":"\\u263"}', 'abc') = ''
SELECT JSONExtractString('{"abc":"hello}', 'abc') = ''
JSONExtract(json[, indices_or_keys…], Return_type)
Парсит JSON и извлекает значение с заданным типом данных.
Это обобщение предыдущих функций JSONExtract<type>
.
Это означает
JSONExtract(..., 'String')
выдает такой же результат, как JSONExtractString()
,
JSONExtract(..., 'Float64')
выдает такой же результат, как JSONExtractFloat()
.
Примеры:
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(String, Array(Float64))') = ('hello',[-100,200,300])
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(b Array(Float64), a String)') = ([-100,200,300],'hello')
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Nullable(Int8))') = [-100, NULL, NULL]
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 4, 'Nullable(Int64)') = NULL
SELECT JSONExtract('{"passed": true}', 'passed', 'UInt8') = 1
SELECT JSONExtract('{"day": "Thursday"}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)') = 'Thursday'
SELECT JSONExtract('{"day": 5}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)') = 'Friday'
JSONExtractKeysAndValues(json[, indices_or_keys…], Value_type)
Разбор пар ключ-значение из JSON, где значение имеет тип данных ClickHouse.
Пример:
SELECT JSONExtractKeysAndValues('{"x": {"a": 5, "b": 7, "c": 11}}', 'x', 'Int8') = [('a',5),('b',7),('c',11)];
JSONExtractKeys
Парсит строку JSON и извлекает ключи.
Синтаксис
JSONExtractKeys(json[, a, b, c...])
Аргументы
json
— строка, содержащая валидный JSON.a, b, c...
— индексы или ключи, разделенные запятыми, которые указывают путь к внутреннему полю во вложенном объекте JSON. Каждый аргумент может быть либо строкой для получения поля по ключу, либо целым числом для получения N-го поля (индексирование начинается с 1, отрицательные числа используются для отсчета с конца). Если параметр не задан, весь JSON разбирается как объект верхнего уровня. Необязательный параметр.
Возвращаемые значения
Массив с ключами JSON.
Пример
Запрос:
SELECT JSONExtractKeys('{"a": "hello", "b": [-100, 200.0, 300]}');
Результат:
text
┌─JSONExtractKeys('{"a": "hello", "b": [-100, 200.0, 300]}')─┐
│ ['a','b'] │
└────────────────────────────────────────────────────────────┘
JSONExtractRaw(json[, indices_or_keys]…)
Возвращает часть JSON в виде строки, содержащей неразобранную подстроку.
Если значение не существует, то возвращается пустая строка.
Пример:
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = '[-100, 200.0, 300]';
JSONExtractArrayRaw(json[, indices_or_keys]…)
Возвращает массив из элементов JSON массива, каждый из которых представлен в виде строки с неразобранными подстроками из JSON.
Если значение не существует или не является массивом, то возвращается пустой массив.
Пример:
SELECT JSONExtractArrayRaw('{"a": "hello", "b": [-100, 200.0, "hello"]}', 'b') = ['-100', '200.0', '"hello"']';
JSONExtractKeysAndValuesRaw
Извлекает необработанные данные из объекта JSON.
Синтаксис
JSONExtractKeysAndValuesRaw(json[, p, a, t, h])
Аргументы
json
— строка, содержащая валидный JSON.p, a, t, h
— индексы или ключи, разделенные запятыми, которые указывают путь к внутреннему полю во вложенном объекте JSON. Каждый аргумент может быть либо строкой для получения поля по ключу, либо целым числом для получения N-го поля (индексирование начинается с 1, отрицательные числа используются для отсчета с конца). Если параметр не задан, весь JSON парсится как объект верхнего уровня. Необязательный параметр.
Возвращаемые значения
Массив с кортежами
('key', 'value')
. Члены кортежа — строки.Пустой массив, если заданный объект не существует или входные данные не валидный JSON.
Тип: Array(Tuple(String, String).
Примеры
Запрос:
SELECT JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}');
Результат:
┌─JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}')─┐
│ [('a','[-100,200]'),('b','{"c":{"d":"hello","f":"world"}}')] │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
Запрос:
SELECT JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}', 'b');
Результат:
┌─JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}', 'b')─┐
│ [('c','{"d":"hello","f":"world"}')] │
└───────────────────────────────────────────────────────────────────────────────────────────────────┘
Запрос:
SELECT JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}', -1, 'c');
Результат:
┌─JSONExtractKeysAndValuesRaw('{"a": [-100, 200.0], "b":{"c": {"d": "hello", "f": "world"}}}', -1, 'c')─┐
│ [('d','"hello"'),('f','"world"')] │
└───────────────────────────────────────────────────────────────────────────────────────────────────────┘
JSON_EXISTS(json, path)
Если значение существует в документе JSON, то возвращается 1.
Если значение не существует, то возвращается 0.
Пример:
SELECT JSON_EXISTS('{"hello":1}', '$.hello');
SELECT JSON_EXISTS('{"hello":{"world":1}}', '$.hello.world');
SELECT JSON_EXISTS('{"hello":["world"]}', '$.hello[*]');
SELECT JSON_EXISTS('{"hello":["world"]}', '$.hello[0]');
До версии 21.11 порядок аргументов функции был обратный, т.е. JSON_EXISTS(path, json)
JSON_QUERY(json, path)
Парсит JSON и извлекает значение как JSON массив или JSON объект.
Если значение не существует, то возвращается пустая строка.
Пример:
SELECT JSON_QUERY('{"hello":"world"}', '$.hello');
SELECT JSON_QUERY('{"array":[[0, 1, 2, 3, 4, 5], [0, -1, -2, -3, -4, -5]]}', '$.array[*][0 to 2, 4]');
SELECT JSON_QUERY('{"hello":2}', '$.hello');
SELECT toTypeName(JSON_QUERY('{"hello":2}', '$.hello'));
Результат:
["world"]
[0, 1, 4, 0, -1, -4]
[2]
String
До версии 21.11 порядок аргументов функции был обратный, т.е. JSON_QUERY(path, json)
JSON_VALUE(json, path)
Парсит JSON и извлекает значение как JSON скаляр.
Если значение не существует, то возвращается пустая строка.
Пример:
SELECT JSON_VALUE('{"hello":"world"}', '$.hello');
SELECT JSON_VALUE('{"array":[[0, 1, 2, 3, 4, 5], [0, -1, -2, -3, -4, -5]]}', '$.array[*][0 to 2, 4]');
SELECT JSON_VALUE('{"hello":2}', '$.hello');
SELECT toTypeName(JSON_VALUE('{"hello":2}', '$.hello'));
Результат:
world
0
2
String
До версии 21.11 порядок аргументов функции был обратный, т.е. JSON_VALUE(path, json)
toJSONString
Сериализует значение в JSON представление. Поддерживаются различные типы данных и вложенные структуры.
По умолчанию 64-битные целые числа и более (например, UInt64
или Int128
) заключаются в кавычки. Настройка output_format_json_quote_64bit_integers управляет этим поведением.
Специальные значения NaN
и inf
заменяются на null
. Чтобы они отображались, включите настройку output_format_json_quote_denormals.
Когда сериализуется значение Enum, то функция выводит его имя.
Синтаксис
toJSONString(value)
Аргументы
value
— значение, которое необходимо сериализовать. Может быть любого типа.
Возвращаемое значение
- JSON представление значения.
Тип: String.
Пример
Первый пример показывает сериализацию Map. Во втором примере есть специальные значения, обернутые в Tuple.
Запрос:
SELECT toJSONString(map('key1', 1, 'key2', 2));
SELECT toJSONString(tuple(1.25, NULL, NaN, +inf, -inf, [])) SETTINGS output_format_json_quote_denormals = 1;
Результат:
{"key1":1,"key2":2}
[1.25,null,"nan","inf","-inf",[]]
Смотрите также