При работе с XML-данными через механизмы XDTO в 1С разработчики часто сталкиваются с неочевидным поведением платформы. Когда мы пытаемся прочитать список элементов из XML, платформа может вернуть либо коллекцию, либо одиночный объект. Это зависит от количества фактически пришедших данных в файле и описания схемы XSD. Разберем детально, почему возникает ошибка «Итератор для значения не определен» и как написать универсальный код, который будет работать стабильно в любой ситуации. Для профилактики подобных проблем в проектах также полезно проводить анализ конфигураций и расширений на наличие ошибок, связанных с вызовами процедур и функций — для этого есть инструмент автоматического анализа кода конфигураций и расширений 1С.
Для начала проанализируем ситуацию. Когда мы используем ФабрикаXDTO.ПрочитатьXML() или обращаемся к свойству ОбъектXDTO, 1С анализирует метаданные. Если в схеме XSD для конкретного узла указано свойство maxOccurs="1", то значение всегда будет трактоваться как одиночный ОбъектXDTO. Однако, если указано maxOccurs="unbounded" (не ограничено) или число больше 1, ситуация становится интереснее.
Если в XML-файле присутствует несколько одноименных узлов, 1С преобразует их в СписокXDTO. По этому списку можно пройтись циклом Для Каждого. Но если в файле пришел всего один такой узел, платформа (особенно при динамическом чтении без жесткой привязки к схеме) может вернуть просто ОбъектXDTO. У одиночного объекта нет итератора, поэтому попытка выполнить цикл Для Каждого Элемент Из Данные приводит к аварийному завершению программы. На практике такие ситуации часто встречаются, когда выполняется загрузка данных из XML-файлов со сложной вложенной структурой, например, при работе с адресными классификаторами.
Самый простой и быстрый способ решить проблему — проверить тип полученных данных перед началом цикла. В 1С для коллекций XDTO существует специальный тип СписокXDTO. Проанализируем пример кода, который позволяет безопасно обработать данные:
Если ТипЗнч(Данные.Строки) = Тип("СписокXDTO") Тогда
// Если это список, просто обходим его
Для Каждого Строка Из Данные.Строки Цикл
ОбработатьСтроку(Строка);
КонецЦикла;
ИначеЕсли ТипЗнч(Данные.Строки) = Тип("ОбъектXDTO") Тогда
// Если это одиночный объект, обрабатываем его без цикла
ОбработатьСтроку(Данные.Строки);
КонецЕсли;
Этот метод надежен, но он заставляет нас дублировать вызов процедуры ОбработатьСтроку(). Рассмотрим подробнее, как сделать этот процесс более изящным.
Чтобы не писать разные блоки кода для одного элемента и для списка, мы можем принудительно привести любое значение к обычному массиву 1С. Это наиболее рекомендуемый подход, так как он позволяет использовать один единственный цикл Для Каждого в основной логике программы. Такой подход часто применяется, когда реализуется выгрузка и загрузка данных XML с использованием гибких фильтров и отборов — для этого подойдёт универсальная выгрузка данных в XML и JSON.
Выясним, как реализовать такую функцию-помощник. Рассмотрим следующий пример:
// Функция преобразует СписокXDTO или одиночный ОбъектXDTO в массив
Функция ПолучитьМассивИзXDTO(ЗначениеXDTO)
Результат = Новый Массив;
Если ЗначениеXDTO = Неопределено Тогда
Возврат Результат;
КонецЕсли;
Если ТипЗнч(ЗначениеXDTO) = Тип("СписокXDTO") Тогда
// Для списка XDTO можно использовать метод Выгрузить() или простой обход
Для Каждого Элемент Из ЗначениеXDTO Цикл
Результат.Добавить(Элемент);
КонецЦикла;
Иначе
// Если это одиночный объект, просто добавляем его в массив
Результат.Добавить(ЗначениеXDTO);
КонецЕсли;
Возврат Результат;
КонецФункции
Теперь в основном коде мы можем действовать уверенно:
КоллекцияДляОбхода = ПолучитьМассивИзXDTO(Данные.Строки);
Для Каждого Элемент Из КоллекцияДляОбхода Цикл
// Здесь код будет работать всегда: и для 0, и для 1, и для 100 элементов
Сообщить(Элемент.Наименование);
КонецЦикла;
Если вы хотите подойти к задаче максимально профессионально, можно обратиться к свойствам самой фабрики XDTO. У каждого объекта XDTO есть метод Свойства(), который возвращает описание модели данных. Мы можем проверить свойство ВерхняяГраница у метаданных конкретного поля. Это особенно важно, если вами предварительно была выполнена конвертация схемы XSD в модель XDTO для работы со сторонними сервисами.
Проанализируем ситуацию: если ВерхняяГраница равна -1, это означает, что поле в принципе является коллекцией (массивом) согласно логике XSD, даже если в данный момент там пусто или один элемент. Однако на практике проверка через ТипЗнч оказывается быстрее в написании и проще в отладке.
Посмотрим на ситуацию шире. Если вы используете XDTO для последующей конвертации данных в JSON, проблема типа данных становится критической. В JSON массив должен быть заключен в квадратные скобки []. Если 1С воспримет один элемент как объект, то в JSON он попадет в фигурных скобках {}, и внешняя система может не распознать данные. Правильный экспорт любых документов базы в JSON требует строгого соблюдения структуры массивов.
Также стоит помнить, что XML-данные можно не только читать, но и визуализировать. Если вам нужно превратить скучный XML в красивый отчет, можно использовать XSLT-преобразования прямо в 1С.
Помните: Чтобы гарантировать получение списка при чтении XML, всегда старайтесь использовать ФабрикаXDTO.ПрочитатьXML() с указанием конкретного типа объекта из пакета XDTO, импортированного в конфигурацию. Если схема загружена корректно и в ней указано maxOccurs="unbounded", платформа будет стараться возвращать СписокXDTO даже для одного элемента.
Мы разберем основные шаги для построения отказоустойчивого алгоритма:
Неопределено. Попытка получить тип от этого значения не вызовет ошибку, но цикл не сработает.СписокXDTO.ОбщегоНазначения.ЗначениеВМассиве()), чтобы быстро приводить одиночные объекты к коллекции.minOccurs="0" и maxOccurs="unbounded" для табличных частей и списков.Используя предложенные методы, вы навсегда избавитесь от ошибки итератора и сделаете ваш код обработки XML-данных через XDTO чистым и надежным.