При интеграции 1С с внешними веб-сервисами, такими как системы IP-телефонии (например, Binotel), платежные шлюзы или маркетплейсы, разработчики часто сталкиваются с форматом передачи данных application/x-www-form-urlencoded. Особенность этого формата заключается в том, что многомерные данные (массивы и объекты) передаются в виде одной «плоской» строки, где вложенность обозначается с помощью квадратных скобок: callDetails[customerData][id]=19457215. Рассмотрим подробно, как правильно преобразовать такую строку в привычные нам Структура, Массив или Соответствие.
В платформе 1С существуют встроенные средства для работы со строками и URL, такие как РаскодироватьСтроку, но нет готовой функции, которая автоматически развернет «путь» в скобках в дерево объектов. Простой разбор строки по разделителям & и = даст нам плоский список ключей, который крайне неудобен для дальнейшей обработки бизнес-логики. Нам же необходимо получить иерархическую структуру, где callDetails будет ключом, значением которого является другая структура.
Если ваша задача не предполагает глубокой вложенности и вам нужно просто получить значения параметров, мы можем воспользоваться комбинацией функций СтрРазделить и РаскодироватьСтроку. Проанализируем пример кода, который позволяет быстро преобразовать строку в Соответствие:
&НаСервере
Функция ЗапросВСоответствие(Знач Данные)
Результат = Новый Соответствие();
// Разделяем строку на пары "ключ=значение" по символу амперсанда
МассивПар = СтрРазделить(Данные, "&");
Для Каждого ТекущаяПара Из МассивПар Цикл
// Разделяем пару на ключ и само значение
Части = СтрРазделить(ТекущаяПара, "=");
Если Части.Количество() >= 2 Тогда
Ключ = РаскодироватьСтроку(Части[0], СпособКодированияСтроки.КодировкаURL);
Значение = РаскодироватьСтроку(Части[1], СпособКодированияСтроки.КодировкаURL);
Результат.Вставить(Ключ, Значение);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Важный нюанс: при работе с URL-кодированными данными пробел может передаваться как символ +. Встроенная функция РаскодироватьСтроку не всегда заменяет плюсы на пробелы автоматически (зависит от версии платформы), поэтому рекомендуется предварительно использовать СтрЗаменить(Строка, "+", " ").
Для обработки строк вида 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 по своей природе текстовая, все значения будут иметь тип Строка. Выясним, на что нужно обратить внимание при дальнейшей работе:
"0" или "18628" нужно явно преобразовывать функциями Число() или сравнивать со строками "true"/"false".Дата(1970, 1, 1, 0, 0, 0) + Число(ЗначениеИзЗапроса).historyData[0] индекс передается как строка "0". Если вам критически важно получить именно объект типа Массив в 1С, потребуется дополнительная постобработка Соответствия, которая проверит ключи на возможность преобразования в число и перенесет данные в Массив.Разберем важный момент: ограничение длины строки. Хотя спецификации HTTP допускают передачу больших объемов данных, старые системы или прокси-серверы могут обрезать GET-запросы длиннее 2048 символов. Если вы проектируете систему, которая отправляет такие данные в 1С, всегда отдавайте предпочтение методу POST с телом в формате application/x-www-form-urlencoded. В этом случае в HTTP-сервисе 1С вы получите данные методом Запрос.ПолучитьТелоКакСтроку() и сможете обработать их предложенными выше функциями без риска потери данных.