Как преобразовать сложную Query String со вложенными структурами в данные 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними веб-сервисами, такими как системы IP-телефонии (например, Binotel), платежные шлюзы или маркетплейсы, разработчики часто сталкиваются с форматом передачи данных application/x-www-form-urlencoded. Особенность этого формата заключается в том, что многомерные данные (массивы и объекты) передаются в виде одной «плоской» строки, где вложенность обозначается с помощью квадратных скобок: callDetails[customerData][id]=19457215. Рассмотрим подробно, как правильно преобразовать такую строку в привычные нам Структура, Массив или Соответствие.

Проблема стандартных методов

В платформе 1С существуют встроенные средства для работы со строками и URL, такие как РаскодироватьСтроку, но нет готовой функции, которая автоматически развернет «путь» в скобках в дерево объектов. Простой разбор строки по разделителям & и = даст нам плоский список ключей, который крайне неудобен для дальнейшей обработки бизнес-логики. Нам же необходимо получить иерархическую структуру, где callDetails будет ключом, значением которого является другая структура.

Вариант 1: Разбор простой (плоской) строки запроса

Если ваша задача не предполагает глубокой вложенности и вам нужно просто получить значения параметров, мы можем воспользоваться комбинацией функций СтрРазделить и РаскодироватьСтроку. Проанализируем пример кода, который позволяет быстро преобразовать строку в Соответствие:


&НаСервере
Функция ЗапросВСоответствие(Знач Данные)
    Результат = Новый Соответствие();
    // Разделяем строку на пары "ключ=значение" по символу амперсанда
    МассивПар = СтрРазделить(Данные, "&");
    
    Для Каждого ТекущаяПара Из МассивПар Цикл
        // Разделяем пару на ключ и само значение
        Части = СтрРазделить(ТекущаяПара, "=");
        Если Части.Количество() >= 2 Тогда
            Ключ = РаскодироватьСтроку(Части[0], СпособКодированияСтроки.КодировкаURL);
            Значение = РаскодироватьСтроку(Части[1], СпособКодированияСтроки.КодировкаURL);
            Результат.Вставить(Ключ, Значение);
        КонецЕсли;
    КонецЦикла;
    
    Возврат Результат;
КонецФункции

Важный нюанс: при работе с URL-кодированными данными пробел может передаваться как символ +. Встроенная функция РаскодироватьСтроку не всегда заменяет плюсы на пробелы автоматически (зависит от версии платформы), поэтому рекомендуется предварительно использовать СтрЗаменить(Строка, "+", " ").

Вариант 2: Рекурсивный парсинг сложных структур (универсальный метод)

Для обработки строк вида callDetails[historyData][0][waitsec]=43 нам потребуется более продвинутый алгоритм. Рассмотрим изящное решение, которое использует преобразование элементов строки в формат JSON, который затем легко считывается встроенными средствами 1С. Проанализируем этот подход по шагам.

Суть метода заключается в том, чтобы превратить строку a[b][c]=val в JSON-строку {"a":{"b":{"c":"val"}}}, а затем объединить эти части в один объект.


Функция РазобратьСложнуюСтрокуЗапроса(Знач ВходнаяСтрока)
    Результат = Новый Соответствие;
    Чтение = Новый ЧтениеJSON;
    
    // Очищаем строку от возможных артефактов и разделяем параметры
    Параметры = СтрРазделить(ВходнаяСтрока, "&", Ложь);
    
    Для Каждого Параметр Из Параметры Цикл
        // Подготавливаем строку для трансформации в JSON
        // Заменяем скобки и знак равенства на JSON-разделители
        ВременнаяСтрока = СтрЗаменить(Параметр, "[", """:{""");
        ВременнаяСтрока = СтрЗаменить(ВременнаяСтрока, "]", "");
        ВременнаяСтрока = СтрЗаменить(ВременнаяСтрока, "=", """:""");
        
        // Формируем JSON-объект и закрываем все открытые фигурные скобки
        КоличествоЗакрывающихСкобок = СтрЧислоВхождений(Параметр, "[");
        JSONСтрока = "{""" + ВременнаяСтрока + """";
        Для Итератор = 0 По КоличествоЗакрывающихСкобок - 1 Цикл
            JSONСтрока = JSONСтрока + "}";
        КонецЦикла;
        JSONСтрока = JSONСтрока + "}";
        
        // Читаем полученный микро-JSON
        Чтение.УстановитьСтроку(JSONСтрока);
        ОбъектПараметра = ПрочитатьJSON(Чтение, Истина);
        
        // Рекурсивно сливаем полученный объект в общий результат
        Для Каждого КлючЗначение Из ОбъектПараметра Цикл
            Ключ = ДекодироватьКомпонент(КлючЗначение.Ключ);
            ДобавитьВКоллекциюРекурсивно(Результат, Ключ, КлючЗначение.Значение);
        КонецЦикла;
    КонецЦикла;
    
    Возврат Результат;
КонецФункции

Для того чтобы данные корректно объединялись (а не перезаписывались), нам понадобится вспомогательная рекурсивная процедура ДобавитьВКоллекциюРекурсивно. Она проверяет, существует ли уже такой ключ, и если да — «углубляется» дальше.


Процедура ДобавитьВКоллекциюРекурсивно(Приемник, Ключ, Значение)
    ТекущееСодержимое = Приемник[Ключ];
    
    Если ТипЗнч(ТекущееСодержимое) = Тип("Соответствие") Тогда
        // Если ветка уже существует, проходим по всем вложенным ключам
        Для Каждого Элемент Из Значение Цикл
            ДобавитьВКоллекциюРекурсивно(ТекущееСодержимое, Элемент.Ключ, Элемент.Значение);
        КонецЦикла;
    ИначеЕсли ТипЗнч(Значение) = Тип("Соответствие") Тогда
        // Если это новая ветка, создаем её
        НовыйУзел = Новый Соответствие;
        Для Каждого Элемент Из Значение Цикл
            ДобавитьВКоллекциюРекурсивно(НовыйУзел, Элемент.Ключ, Элемент.Значение);
        КонецЦикла;
        Приемник.Вставить(Ключ, НовыйУзел);
    Иначе
        // Базовый случай: записываем конечное значение
        Приемник.Вставить(Ключ, ДекодироватьКомпонент(Значение));
    КонецЕсли;
КонецПроцедуры

Функция ДекодироватьКомпонент(Знач Значение)
    // Обязательная обработка плюсов перед декодированием URL
    Результат = СтрЗаменить(Значение, "+", " ");
    Возврат РаскодироватьСтроку(Результат, СпособКодированияСтроки.КодировкаURL);
КонецФункции

Особенности обработки типов данных

Проанализируем ситуацию с типами данных, которые мы получаем на выходе. Поскольку Query String по своей природе текстовая, все значения будут иметь тип Строка. Выясним, на что нужно обратить внимание при дальнейшей работе:

  1. Числа и булево: Значения вроде "0" или "18628" нужно явно преобразовывать функциями Число() или сравнивать со строками "true"/"false".
  2. Даты в формате Unix: В системах телефонии время часто передается как количество секунд с 01.01.1970 (Unix Timestamp). В 1С такую дату можно получить выражением: Дата(1970, 1, 1, 0, 0, 0) + Число(ЗначениеИзЗапроса).
  3. Массивы с цифровыми индексами: В примере historyData[0] индекс передается как строка "0". Если вам критически важно получить именно объект типа Массив в 1С, потребуется дополнительная постобработка Соответствия, которая проверит ключи на возможность преобразования в число и перенесет данные в Массив.

Рекомендации по безопасности

Разберем важный момент: ограничение длины строки. Хотя спецификации HTTP допускают передачу больших объемов данных, старые системы или прокси-серверы могут обрезать GET-запросы длиннее 2048 символов. Если вы проектируете систему, которая отправляет такие данные в 1С, всегда отдавайте предпочтение методу POST с телом в формате application/x-www-form-urlencoded. В этом случае в HTTP-сервисе 1С вы получите данные методом Запрос.ПолучитьТелоКакСтроку() и сможете обработать их предложенными выше функциями без риска потери данных.

← На главную