Как правильно обойти коллекцию XDTO, если в ней может быть один или несколько элементов?

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

При работе с XML-данными через механизмы XDTO в 1С разработчики часто сталкиваются с неочевидным поведением платформы. Когда мы пытаемся прочитать список элементов из XML, платформа может вернуть либо коллекцию, либо одиночный объект. Это зависит от количества фактически пришедших данных в файле и описания схемы XSD. Разберем детально, почему возникает ошибка «Итератор для значения не определен» и как написать универсальный код, который будет работать стабильно в любой ситуации. Для профилактики подобных проблем в проектах также полезно проводить анализ конфигураций и расширений на наличие ошибок, связанных с вызовами процедур и функций — для этого есть инструмент автоматического анализа кода конфигураций и расширений 1С.

Почему возникает проблема: разберем причину

Для начала проанализируем ситуацию. Когда мы используем ФабрикаXDTO.ПрочитатьXML() или обращаемся к свойству ОбъектXDTO, 1С анализирует метаданные. Если в схеме XSD для конкретного узла указано свойство maxOccurs="1", то значение всегда будет трактоваться как одиночный ОбъектXDTO. Однако, если указано maxOccurs="unbounded" (не ограничено) или число больше 1, ситуация становится интереснее.

Если в XML-файле присутствует несколько одноименных узлов, 1С преобразует их в СписокXDTO. По этому списку можно пройтись циклом Для Каждого. Но если в файле пришел всего один такой узел, платформа (особенно при динамическом чтении без жесткой привязки к схеме) может вернуть просто ОбъектXDTO. У одиночного объекта нет итератора, поэтому попытка выполнить цикл Для Каждого Элемент Из Данные приводит к аварийному завершению программы. На практике такие ситуации часто встречаются, когда выполняется загрузка данных из XML-файлов со сложной вложенной структурой, например, при работе с адресными классификаторами.

Способ 1: Проверка типа значения (Классический подход)

Самый простой и быстрый способ решить проблему — проверить тип полученных данных перед началом цикла. В 1С для коллекций XDTO существует специальный тип СписокXDTO. Проанализируем пример кода, который позволяет безопасно обработать данные:


Если ТипЗнч(Данные.Строки) = Тип("СписокXDTO") Тогда
    // Если это список, просто обходим его
    Для Каждого Строка Из Данные.Строки Цикл
        ОбработатьСтроку(Строка);
    КонецЦикла;
ИначеЕсли ТипЗнч(Данные.Строки) = Тип("ОбъектXDTO") Тогда
    // Если это одиночный объект, обрабатываем его без цикла
    ОбработатьСтроку(Данные.Строки);
КонецЕсли;

Этот метод надежен, но он заставляет нас дублировать вызов процедуры ОбработатьСтроку(). Рассмотрим подробнее, как сделать этот процесс более изящным.

Способ 2: Универсальная нормализация в массив

Чтобы не писать разные блоки кода для одного элемента и для списка, мы можем принудительно привести любое значение к обычному массиву 1С. Это наиболее рекомендуемый подход, так как он позволяет использовать один единственный цикл Для Каждого в основной логике программы. Такой подход часто применяется, когда реализуется выгрузка и загрузка данных XML с использованием гибких фильтров и отборов — для этого подойдёт универсальная выгрузка данных в XML и JSON.

Выясним, как реализовать такую функцию-помощник. Рассмотрим следующий пример:


// Функция преобразует СписокXDTO или одиночный ОбъектXDTO в массив
Функция ПолучитьМассивИзXDTO(ЗначениеXDTO)
    
    Результат = Новый Массив;
    
    Если ЗначениеXDTO = Неопределено Тогда
        Возврат Результат;
    КонецЕсли;
    
    Если ТипЗнч(ЗначениеXDTO) = Тип("СписокXDTO") Тогда
        // Для списка XDTO можно использовать метод Выгрузить() или простой обход
        Для Каждого Элемент Из ЗначениеXDTO Цикл
            Результат.Добавить(Элемент);
        КонецЦикла;
    Иначе
        // Если это одиночный объект, просто добавляем его в массив
        Результат.Добавить(ЗначениеXDTO);
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

Теперь в основном коде мы можем действовать уверенно:


КоллекцияДляОбхода = ПолучитьМассивИзXDTO(Данные.Строки);

Для Каждого Элемент Из КоллекцияДляОбхода Цикл
    // Здесь код будет работать всегда: и для 0, и для 1, и для 100 элементов
    Сообщить(Элемент.Наименование);
КонецЦикла;

Способ 3: Использование метаданных XDTO

Если вы хотите подойти к задаче максимально профессионально, можно обратиться к свойствам самой фабрики XDTO. У каждого объекта XDTO есть метод Свойства(), который возвращает описание модели данных. Мы можем проверить свойство ВерхняяГраница у метаданных конкретного поля. Это особенно важно, если вами предварительно была выполнена конвертация схемы XSD в модель XDTO для работы со сторонними сервисами.

Проанализируем ситуацию: если ВерхняяГраница равна -1, это означает, что поле в принципе является коллекцией (массивом) согласно логике XSD, даже если в данный момент там пусто или один элемент. Однако на практике проверка через ТипЗнч оказывается быстрее в написании и проще в отладке.

Важные нюансы при работе с JSON

Посмотрим на ситуацию шире. Если вы используете XDTO для последующей конвертации данных в JSON, проблема типа данных становится критической. В JSON массив должен быть заключен в квадратные скобки []. Если 1С воспримет один элемент как объект, то в JSON он попадет в фигурных скобках {}, и внешняя система может не распознать данные. Правильный экспорт любых документов базы в JSON требует строгого соблюдения структуры массивов.

Также стоит помнить, что XML-данные можно не только читать, но и визуализировать. Если вам нужно превратить скучный XML в красивый отчет, можно использовать XSLT-преобразования прямо в 1С.

Помните: Чтобы гарантировать получение списка при чтении XML, всегда старайтесь использовать ФабрикаXDTO.ПрочитатьXML() с указанием конкретного типа объекта из пакета XDTO, импортированного в конфигурацию. Если схема загружена корректно и в ней указано maxOccurs="unbounded", платформа будет стараться возвращать СписокXDTO даже для одного элемента.

Резюме и рекомендации

Мы разберем основные шаги для построения отказоустойчивого алгоритма:

  1. Проверяйте на Неопределено. Если тег в XML отсутствует, обращение к свойству XDTO вернет Неопределено. Попытка получить тип от этого значения не вызовет ошибку, но цикл не сработает.
  2. Используйте типизацию. Всегда проверяйте, является ли значение типом СписокXDTO.
  3. Применяйте обертки. Создайте общую функцию в общем модуле (например, ОбщегоНазначения.ЗначениеВМассиве()), чтобы быстро приводить одиночные объекты к коллекции.
  4. Следите за схемой. Если вы сами проектируете XSD, всегда устанавливайте minOccurs="0" и maxOccurs="unbounded" для табличных частей и списков.

Используя предложенные методы, вы навсегда избавитесь от ошибки итератора и сделаете ваш код обработки XML-данных через XDTO чистым и надежным.

← На главную