При работе с API и обмене данными в 1С 7.7 часто возникает задача парсинга JSON. Встроенных средств для этого в платформе нет, а сторонние парсеры могут не справляться с большими объемами данных и приводить к падению программы. Отличной альтернативой является использование внешней компоненты SQLite, которая позволяет обрабатывать даже очень большие JSON-файлы прямо в памяти.
Однако при таком подходе возникает неочевидная проблема: SQLite по умолчанию преобразует булевы значения true и false из JSON в числа 1 и 0. Это создает неоднозначность: как отличить логическое "Да" (true), ставшее единицей, от числового значения 1? Особенно это критично, когда структура JSON динамическая и заранее неизвестно, какие поля являются логическими, а какие — числовыми.
Рассмотрим, как решить эту проблему элегантно и надежно, используя возможности самой SQLite.
Для начала убедимся, что у нас есть современная версия внешней компоненты 1sqlite.dll, так как функции для работы с JSON появились в более свежих сборках. Основная работа по разбору JSON ложится на табличную функцию json_tree.
Она принимает на вход текстовую строку с JSON и возвращает таблицу, в которой каждый узел JSON-документа представлен отдельной строкой. Это позволяет работать с JSON как с обычной таблицей с помощью SQL-запросов.
Вот пример базовой функции на 1С 7.7 для парсинга JSON в ТаблицуЗначений:
Функция РазборJSON_SQLLite(ТекстJSON)
Попытка
База = СоздатьОбъект("SQLiteBase");
Исключение
ЗагрузитьВнешнююКомпоненту("1sqlite.dll");
База = СоздатьОбъект("SQLiteBase");
КонецПопытки;
// Открываем базу данных в оперативной памяти
База.Открыть(":memory:");
Запрос = База.НовыйЗапрос();
Запрос.Подготовить("
|SELECT
| *
|FROM json_tree(@ТекстJSON)
|");
Запрос.УстановитьПараметр("@ТекстJSON", ТекстJSON);
тзДерево = Запрос.Выполнить();
Возврат тзДерево;
КонецФункции
Выполнив этот код, мы получим таблицу со всеми элементами JSON, но столкнемся с описанной выше проблемой: все значения true будут представлены как 1.
Секрет правильного определения типов данных кроется в структуре таблицы, которую возвращает функция json_tree. В современных версиях SQLite она содержит не только ключ (key) и значение (value), но и, что самое важное, колонку с типом данных (type).
Эта колонка для каждой строки явно указывает на исходный тип значения в JSON. Возможные значения в ней:
'true' — для логического значения true.'false' — для логического значения false.'integer' — для целого числа.'real' — для вещественного числа.'text' — для строки.'object' — для объекта (структуры).'array' — для массива.'null' — для значения null.Имея эту информацию, мы можем легко отличить логическую единицу от числовой. Если в колонке value стоит 1, а в колонке type — 'true', значит, это было булево значение. Если же в type стоит 'integer', то это обычное число.
Можно было бы получать таблицу с колонкой type в 1С и уже в коде анализировать ее, преобразуя значения. Но есть способ лучше — сделать всю работу по преобразованию прямо в SQL-запросе с помощью оператора CASE. Это и производительнее, и делает код в 1С чище.
Модифицируем наш запрос так, чтобы он сразу возвращал готовые к использованию данные: "Да" для true, "Нет" для false, и исходное значение для всех остальных типов.
Посмотрим на финальный код функции:
Функция РазборJSON_SQLLite(ТекстJSON)
Попытка
База = СоздатьОбъект("SQLiteBase");
Исключение
ЗагрузитьВнешнююКомпоненту("1sqlite.dll");
База = СоздатьОбъект("SQLiteBase");
КонецПопытки;
База.Открыть(":memory:");
Запрос = База.НовыйЗапрос();
Запрос.Подготовить("
|SELECT
| -- Используем оператор CASE для преобразования значений
| CASE
| WHEN tree.type == 'true' THEN 'Да'
| WHEN tree.type == 'false' THEN 'Нет'
| ELSE tree.value
| END AS Значение,
| tree.key AS Ключ,
| tree.path AS Путь,
| tree.type AS Тип
|FROM json_tree(@ТекстJSON) AS tree
|WHERE tree.key > '' // Отбираем только узлы с непустыми ключами
|");
Запрос.УстановитьПараметр("@ТекстJSON", ТекстJSON);
тзДерево = Запрос.Выполнить();
Возврат тзДерево;
КонецФункции
Что делает этот запрос:
CASE ... END AS Значение: Создает вычисляемую колонку "Значение".WHEN tree.type == 'true' THEN 'Да': Если тип данных — 'true', в колонку "Значение" записывается строка "Да".WHEN tree.type == 'false' THEN 'Нет': Если тип — 'false', записывается "Нет".ELSE tree.value: Во всех остальных случаях в колонку "Значение" записывается исходное значение из колонки value.Теперь в результате мы получаем ТаблицуЗначений, где булевы значения уже представлены в удобном для пользователя виде, а числовые значения остались числами. Проблема решена полностью и очень эффективно.
Этот метод отлично зарекомендовал себя при работе с большими объемами данных, например, при получении ответов от API "Честный Знак", где размер JSON-ответа может достигать нескольких мегабайт. SQLite обрабатывает такие объемы практически мгновенно, не вызывая падения 1С.