При интеграции 1С с внешними информационными системами разработчики регулярно сталкиваются с необходимостью обработки сложных, многократно вложенных коллекций данных. Чаще всего такие структуры приходят в формате JSON или XML в результате выполнения HTTP-запросов или экспорта из сторонних баз данных. Нам может прийти массив, элементами которого являются структуры, содержащие в себе другие массивы, внутри которых снова находятся структуры, и так далее до бесконечности.
В этой статье мы подробно разберем, как правильно и в соответствии со стандартами разработки 1С спроектировать алгоритм разбора таких данных. Мы изучим ключевые ограничения платформы, напишем универсальный шаблон рекурсивного обхода коллекций, рассмотрим готовые инструменты Библиотеки стандартных подсистем (БСП) и узнаем, когда ручные циклы стоит заменить на строго типизированную модель XDTO.
Разберем типичную архитектурную ошибку, которую совершают многие разработчики при парсинге вложенных данных (особенно JSON). По умолчанию для представления объектов с парами "ключ-значение" в 1С используется объект Структура. Однако у этого объекта есть критическое ограничение платформы.
Ключ структуры в 1С обязан соответствовать требованиям, предъявляемым к именам переменных. Это значит, что ключ структуры:
Если во входящем файле обмена содержатся подобные ключи, попытка десериализовать их напрямую в Структуру вызовет фатальное исключение в рантайме.
Выясним причину решения этой проблемы. Чтобы обойти это ограничение, стандарты разработки 1С предписывают использовать объект Соответствие вместо Структуры. Ключами соответствия могут быть любые типы данных, в том числе строки произвольного формата со спецсимволами, цифрами и пробелами.
Рассмотрим пример безопасного чтения входящего потока JSON с помощью встроенных средств платформы:
// Создаем чтение JSON и открываем файл или строку
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл(ПутьКФайлу);
// Вторым параметром передаем Истина, чтобы платформа прочитала объекты JSON как Соответствия, а не Структуры
ДанныеОбмена = ПрочитатьJSON(ЧтениеJSON, Истина);
ЧтениеJSON.Закрыть();
Использование этого простого правила гарантирует, что ваша обработка импорта не "упадет" при первом же нетипичном наименовании свойства во внешней системе.
Посмотрим на ситуацию, когда глубина вложенности массивов и структур заранее неизвестна или может динамически меняться. Написать плоские циклы Для каждого ... Из ... для каждого уровня вложенности в таком случае невозможно — код превратится в громоздкую и нечитаемую конструкцию "спагетти".
Решением является написание рекурсивной процедуры. Давайте разберем по шагам универсальный шаблон обхода входящих коллекций любой сложности:
&НаСервере
Процедура РазобратьКоллекциюДанных(Знач Данные)
ТипДанных = ТипЗнч(Данные);
// Шаг 1: Если перед нами Массив — перебираем его элементы циклом
Если ТипДанных = Тип("Массив") Тогда
Для Каждого ЭлементКоллекции Из Данные Цикл
// Рекурсивно вызываем эту же процедуру для каждого элемента
РазобратьКоллекциюДанных(ЭлементКоллекции);
КонецЦикла;
// Шаг 2: Если перед нами Структура или Соответствие — перебираем пары Ключ-Значение
ИначеЕсли ТипДанных = Тип("Структура") Или ТипДанных = Тип("Соответствие") Тогда
Для Каждого КлючИЗначение Из Данные Цикл
ТекущийКлюч = КлючИЗначение.Ключ;
ТекущееЗначение = КлючИЗначение.Значение;
// Бизнес-логика: обрабатываем конкретные узлы, которые нам известны по схеме импорта
Если ТекущийКлюч = "ИмяНужногоУзла" Тогда
ОбработатьЦелевыеДанные(ТекущееЗначение);
КонецЕсли;
// Продолжаем рекурсивный спуск по дереву объектов
РазобратьКоллекциюДанных(ТекущееЗначение);
КонецЦикла;
// Шаг 3: Базовый случай рекурсии (примитивные типы данных)
Иначе
// Сюда попадают строки, числа, булевы значения и даты.
// На этом этапе мы можем сопоставлять их с реквизитами документов или справочников.
КонецЕсли;
КонецПроцедуры
Этот шаблон безопасен, так как он не зависит от конкретной структуры ваших данных и динамически подстраивается под любой уровень вложенности, не вызывая переполнения стека при разумных объемах импорта.
Разработка по стандартам 1С подразумевает минимизацию написания собственного "велосипедного" кода. В составе Библиотеки стандартных подсистем (БСП) уже есть готовые, оптимизированные функции для работы со сложными коллекциями. Рассмотрим подробнее наиболее полезные из них.
1. Безопасное чтение свойств объектов. Если во входящем JSON-пакете некоторые свойства являются необязательными, обращение к ним напрямую (например, СтруктураДанных.ИмяСвойства) приведет к ошибке "Поле объекта не обнаружено". Для безопасного чтения используйте функцию БСП:
ЗначениеСвойства = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(Данные, "ИмяНеобязательногоСвойства", "ДефолтноеЗначение");
Эта функция вернет значение свойства, если оно существует, либо аккуратно вернет переданное вами значение по умолчанию (в данном случае строку "ДефолтноеЗначение"), не прерывая выполнение алгоритма.
2. Глубокое рекурсивное копирование. Обычное присваивание или стандартный метод Скопировать() для массивов копируют элементы только на первом уровне вложенности (передавая ссылки на вложенные коллекции). Если вам нужно создать полную, независимую копию сложного дерева данных для последующей модификации, воспользуйтесь методом:
ПолнаяКопияДанных = ОбщегоНазначенияКлиентСервер.СкопироватьРекурсивно(ИсходныйСложныйМассив);
3. Преобразование плоских таблиц во вложенные коллекции. Зачастую перед отправкой во внешнюю систему нам нужно выполнить обратную операцию — упаковать имеющуюся в 1С ТаблицуЗначений в массив структур. Напишем это оптимальным образом с помощью БСП:
МассивСтруктур = ОбщегоНазначения.ТаблицаЗначенийВМассив(ТаблицаТоваровДокумента);
Проанализируем ситуацию, при которой входящие данные строго регламентированы, имеют сложную схему и жесткие правила валидации типов. Ручной разбор вложенными циклами и рекурсией в данном случае считается плохим тоном в промышленной разработке.
Стандарты фирмы "1С" для таких задач рекомендуют использовать мощный встроенный механизм XDTO (XML Data Transfer Objects). Схема работы выглядит следующим образом:
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(ПутьКФайлу);
ОбъектДанных = ФабрикаXDTO.ПрочитатьJSON(Чтение, ФабрикаXDTO.Тип("http://www.your-uri.com", "ИмяКорневогоТипа"));
Чтение.Закрыть();
После этого вам больше не нужны сложные рекурсивные функции и постоянные проверки типов. Вы можете обращаться к любым вложенным свойствам напрямую "через точку" с полной уверенностью в безопасности этих операций:
Если ОбъектДанных.Контрагент.Контакты.Строки.Количество() > 0 Тогда
ПервыйКонтакт = ОбъектДанных.Контрагент.Контакты.Строки[0].Значение;
КонецЕсли;
Подводя итог, выделим ключевые правила, которыми нужно руководствоваться при написании распаковщиков сложных массивов в 1С:
Структуры. При разборе произвольных JSON-структур используйте Соответствие.СвойствоСтруктуры) для защиты кода от непредвиденных исключений "Поле объекта не обнаружено".XDTO вместо написания ручных парсеров.