При интеграции систем и разборе выгрузок (например, логов или журнала регистрации) разработчики часто сталкиваются с чтением XML через фабрику XDTO. Нередко возникает ситуация: метод ФабрикаXDTO.ПрочитатьXML() возвращает коллекцию СписокXDTO, внутри которой лежат элементы типа ОбъектXDTO. Однако при попытке развернуть их в отладчике они кажутся пустыми, а вызовы стандартных методов коллекций приводят к аварийному завершению с ошибкой.
Проанализируем ситуацию, выясним причины такого поведения платформы 1С:Предприятие и разберем по шагам, как правильно извлекать данные из объектов XDTO, а также какие альтернативные подходы к разбору XML существуют.
Для понимания проблемы разберем внутреннюю архитектуру работы механизмов XDTO в платформе 1С:
Количество() вызывают синтаксическую ошибку отсутствия метода. Метод Получить() у объекта XDTO существует, но в качестве параметра он принимает не целочисленный индекс позиции, а имя свойства в виде строки либо объект описания СвойствоXDTO. Передача числа приводит к исключению времени выполнения.<Событие/> или <Событие></Событие>), фабрика XDTO все равно создает полноценный экземпляр типа ОбъектXDTO. При отсутствии дочерних элементов или схемы он не содержит заполненных значений.Посмотрим на пример правильного перебора всех доступных свойств, входящих в состав конкретного ОбъектXDTO. Коллекцией является не сам объект, а результат вызова его метода Свойства(), который возвращает СписокXDTO с описаниями реквизитов.
Рассмотрим код для обхода свойств:
Для Каждого СвойствоXDTO Из ПолеОбъектXDTO.Свойства() Цикл
ИмяСвойства = СвойствоXDTO.Имя;
// Проверяем, заполнено ли свойство в объекте
Если ПолеОбъектXDTO.Установлено(ИмяСвойства) Тогда
Значение = ПолеОбъектXDTO[ИмяСвойства];
// Выполняем обработку значения
КонецЕсли;
КонецЦикла;
Обратите внимание на метод Установлено(): он позволяет избежать ошибок при попытке прочитать опциональное свойство, значение которого не было передано в XML-файле.
Если тег содержит внутри себя просто текст, но был прочитан как нетипизированный ОбъектXDTO, его свойства будут пустыми, а метод Свойства().Количество() вернет ноль. В таком случае значение хранится в виде последовательности XML.
Разберем, как прочитать этот текст программно:
Если ПолеОбъектXDTO.Последовательность().Количество() > 0 Тогда
ТекстЗначения = ПолеОбъектXDTO.Последовательность().ПолучитьТекст(0);
КонецЕсли;
Чтобы фабрика не создавала пустые или универсальные структуры xsd:anyType, при чтении следует передавать ожидаемый тип данных из схемы (пакета XDTO). Если тип задан строго, платформа автоматически сопоставит XML-теги со структурой свойств.
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("имяфайла.xml");
ТипДанных = ФабрикаXDTO.Тип("http://v8.1c.ru/edi/edi_stnd", "ИмяТипаДанных");
Данные = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипДанных);
ЧтениеXML.Закрыть();
Если файл был сформирован штатными механизмами платформы 1С (например, выгрузка в XML через метод ЗаписатьXML()), ручной разбор через ФабрикаXDTO избыточен. Гораздо проще применить СериализаторXDTO, который автоматически восстанавливает прикладные типы 1С (структуры, таблицы значений, ссылки):
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("имяфайла.xml");
Данные = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
ЧтениеXML.Закрыть();
Когда речь идет о журнале регистрации или объемных выгрузках, чтение через ФабрикаXDTO.ПрочитатьXML() или ПостроительDOM может привести к аварийной остановке с ошибкой Недостаточно памяти. Фабрика строит полное дерево объектов в оперативной памяти целиком.
Для обхода больших файлов оптимально использовать прямое последовательное чтение через объект ЧтениеXML. Проанализируем пример такого потокового обхода:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("имяфайла.xml");
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяУзла = Чтение.Имя;
// Читаем атрибуты, если они есть
Пока Чтение.ПрочитатьАтрибут() Цикл
ИмяАтрибута = Чтение.Имя;
ЗначениеАтрибута = Чтение.Значение;
КонецЦикла;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
ЗначениеТекста = Чтение.Значение;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
// Обработка завершения чтения тега
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Такой подход читает XML-файл построчно и фрагментарно, потребляет минимальное количество оперативной памяти и успешно справляется с файлами объемом в сотни мегабайт и гигабайты.