При активной работе с интеграциями в 1С разработчики часто сталкиваются со странной особенностью механизма XDTO. Когда мы считываем XML-документ, содержащий коллекцию элементов, поведение платформы изменяется в зависимости от количества тегов во входящих данных. Если в списке содержится два и более элемента, фабрика XDTO возвращает коллекцию СписокXDTO. Однако если элемент всего один, платформа возвращает одиночный ОбъектXDTO. Рассмотрим подробнее причину такого поведения и проанализируем способы решения этой задачи.
Разберем по шагам, почему фабрика XDTO ведет себя подобным образом. Все зависит от того, используется ли строго заданная схема XML (XSD) и как именно происходит чтение файла:
anyType), платформенный парсер опирается исключительно на фактически встречающиеся теги. Встретив ровно один тег с конкретным именем, парсер интерпретирует его как единственный объект. Если же в файле идут два или более одинаковых тега подряд, платформа определяет их как коллекцию и оборачивает в СписокXDTO.ФабрикаXDTO.ПрочитатьXML() непосредственно для чтения корневого узла результат всегда будет считаться одиночным объектом. Коллекция может существовать только в качестве вложенных элементов родительского объекта.Такое поведение проявляется не только при разборе XML-файлов, но и при вызове SOAP Web-сервисов, а также при преобразовании данных в формат JSON.
Если структура данных описана в схеме XSD и для свойства задано значение верхней границы maxOccurs больше 1 (или unbounded / -1), у платформы 1С есть штатный механизм для гарантированного получения списка.
Вместо прямого обращения к свойству через точку (например, РодительскийОбъект.ИмяСвойства) следует использовать метод ПолучитьСписок(). Рассмотрим пример кода:
// Гарантированно получаем СписокXDTO, независимо от количества элементов (0, 1 или более)
КоллекцияДокументов = РодительскийОбъект.ПолучитьСписок("docum");
Для Каждого ЭлементСписка Из КоллекцияДокументов Цикл
// Обработка элемента
КонецЦикла;
Важное замечание: Метод ПолучитьСписок() вернет объект СписокXDTO даже в том случае, если в XML-файле присутствовал всего один элемент или тег вовсе отсутствовал. Исключение возникнет только тогда, когда в самой XSD-схеме данное свойство объявлено как одиночное (то есть maxOccurs = 1).
На практике часто встречаются ситуации, когда схема XSD недоступна, тип является динамическим или происходит работа с произвольными данными XML. В этом случае прямая передача результата в цикл Для Каждого ... Из ... Цикл вызовет ошибку времени выполнения, если результатом оказался одиночный ОбъектXDTO.
Проанализируем универсальное решение, которое позволяет привести полученный объект или список к стандартному массиву 1С. Поскольку платформа 1С вычисляет выражение после ключевого слова Из в цикле Для Каждого только один раз, мы можем написать легкую функцию-преобразователь:
Функция ПолучитьДанныеСпискаXDTO(ДанныеXDTO)
Если ДанныеXDTO = Неопределено Тогда
Возврат Новый Массив;
ИначеЕсли ТипЗнч(ДанныеXDTO) = Тип("ОбъектXDTO") Тогда
Результат = Новый Массив;
Результат.Добавить(ДанныеXDTO);
Возврат Результат;
Иначе
// Ожидается, что передана коллекция СписокXDTO или Массив
Возврат ДанныеXDTO;
КонецЕсли;
КонецФункции
Посмотрим, как удобно использовать такую функцию в основном коде программы при обходе элементов:
// Чтение XML через фабрику
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("status_out_single.xml");
ДанныеОтвета = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
ЧтениеXML.Закрыть();
// Применяем функцию для безопасного обхода
Для Каждого ЭлементDocum Из ПолучитьДанныеСпискаXDTO(ДанныеОтвета.docum) Цикл
// Код обработки каждого документа
Сообщить(ЭлементDocum.Status);
КонецЦикла;
Другой подход заключается в написании вспомогательной функции для последовательного считывания элементов. Это полезно, когда необходимо обрабатывать элементы в цикле Пока без предварительного создания массива в памяти:
Функция ПрочитатьСледующийЭлементXDTO(Источник, ЗначениеЭлемента, Счетчик)
Если Источник = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
Если ТипЗнч(Источник) = Тип("СписокXDTO") Тогда
Если Счетчик < Источник.Количество() Тогда
ЗначениеЭлемента = Источник.Получить(Счетчик);
Счетчик = Счетчик + 1;
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
ИначеЕсли ТипЗнч(Источник) = Тип("ОбъектXDTO") Тогда
Если Счетчик = 0 Тогда
ЗначениеЭлемента = Источник;
Счетчик = Счетчик + 1;
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
КонецЕсли;
Возврат Ложь;
КонецФункции
Использование функции в практическом коде выглядит следующим образом:
ТекущийЭлемент = Неопределено;
СчетчикЭлементов = 0;
Пока ПрочитатьСледующийЭлементXDTO(ДанныеОтвета.docum, ТекущийЭлемент, СчетчикЭлементов) Цикл
// Выполняем действия с ТекущийЭлемент
Сообщить(ТекущийЭлемент.Код);
КонецЦикла;
Странное на первый взгляд поведение фабрики XDTO при чтении одиночных элементов является особенностью реализации платформы 1С. Для написания надежного и отказоустойчивого кода рекомендуется:
ОбъектXDTO.ПолучитьСписок("ИмяСвойства").ОбъектXDTO к массиву.