При интеграции систем и загрузке данных из внешних источников разработчики 1С часто сталкиваются с нестандартным поведением потокового чтения. Типичная ситуация: при пошаговом чтении документа с помощью ЧтениеXML и функции ПрочитатьXML() часть узлов (например, идентификаторы Ref, реквизиты Parent) оказывается пропущена, а курсор сразу перескакивает к последующим элементам. Разберем по шагам, почему возникает такое поведение платформы, и рассмотрим надежные способы чтения таких файлов.
Проанализируем типовой сценарий, приводящий к ошибке. В исходном коде попытка прочитать XML выглядит следующим образом:
Если ЧтениеХМЛ.ТипУзла <> ТипУзлаXML.НачалоЭлемента Тогда
ВызватьИсключение "Ошибка чтения ХМЛ";
КонецЕсли;
ЧтениеХМЛ.Прочитать();
НоменклатураСсылка = ПрочитатьXML(ЧтениеХМЛ);
// Курсор неожиданно смещается дальше ожидаемой позиции
Выясним основные причины некорректного поведения:
ПрочитатьXML(). Метод ПрочитатьXML() предназначен для десериализации типа данных XDTO или объекта целиком. При вызове этой функции платформа считывает текущий элемент вместе с его содержимым и закрывающим тегом, после чего автоматически переводит внутренний курсор ЧтениеXML на следующий узел. Если разработчик дополнительно вызывает ЧтениеХМЛ.Прочитать(), происходит двойное смещение указателя, и следующий тег пропускается.xmlns="". Если в XML узел содержит пустой атрибут пространства имен xmlns="", но при этом тип указан как xsi:type="CatalogRef.Номенклатура", платформенный сериализатор не может сопоставить тип со схемой конфигурации без явно заданного префикса пространства имен (например, http://v8.1c.ru/8.1/data/enterprise/current-config). В результате функция либо завершается ошибкой, либо считывает пустое значение.ПрочитатьXML(), требует строгого совпадения порядка тегов, типов схемы и позиции курсора. Малейшее отклонение в структуре XML нарушает весь алгоритм загрузки.Рассмотрим наиболее удобный и устойчивый к порядку тегов способ — объектную модель ДокументDOM. Этот вариант идеально подходит для файлов небольшого и среднего объема, когда необходимо обращаться к произвольным элементам структуры по их именам.
Разберем пример чтения узлов номенклатуры с использованием DOM:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Exchange\data.xml");
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
// Получаем коллекцию всех узлов номенклатуры
СписокЭлементов = ДокументDOM.ПолучитьЭлементыПоИмени("CatalogObject.Номенклатура.Вручную");
Для Каждого УзелОбъекта Из СписокЭлементов Цикл
// Получаем значение ссылки (GUID)
УзлыRef = УзелОбъекта.ПолучитьЭлементыПоИмени("Ref");
Если УзлыRef.Количество() > 0 Тогда
СтрокаGUID = УзлыRef[0].ТекстовоеСодержимое;
СсылкаНоменклатуры = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID));
КонецЕсли;
// Получаем признак группы
ЭтоГруппа = Ложь;
УзлыIsFolder = УзелОбъекта.ПолучитьЭлементыПоИмени("IsFolder");
Если УзлыIsFolder.Количество() > 0 Тогда
ЭтоГруппа = (ВРег(СокрЛП(УзлыIsFolder[0].ТекстовоеСодержимое)) = "TRUE");
КонецЕсли;
// Создаем или получаем объект для записи
ОбъектСправочника = СсылкаНоменклатуры.ПолучитьОбъект();
Если ОбъектСправочника = Неопределено Тогда
Если ЭтоГруппа Тогда
ОбъектСправочника = Справочники.Номенклатура.СоздатьГруппу();
Иначе
ОбъектСправочника = Справочники.Номенклатура.СоздатьЭлемент();
КонецЕсли;
ОбъектСправочника.УстановитьСсылкуНового(СсылкаНоменклатуры);
КонецЕсли;
// Заполняем реквизиты
УзлыКод = УзелОбъекта.ПолучитьЭлементыПоИмени("Code");
Если УзлыКод.Количество() > 0 Тогда
ОбъектСправочника.Код = УзлыКод[0].ТекстовоеСодержимое;
КонецЕсли;
УзлыНаименование = УзелОбъекта.ПолучитьЭлементыПоИмени("Description");
Если УзлыНаименование.Количество() > 0 Тогда
ОбъектСправочника.Наименование = УзлыНаименование[0].ТекстовоеСодержимое;
КонецЕсли;
ОбъектСправочника.Записать();
КонецЦикла;
Если файл имеет большой объем (сотни мегабайт или гигабайты), загружать его целиком в память через DOM не рекомендуется из-за высокого расхода оперативной памяти. В этом случае применим классический потоковый обход через ЧтениеXML, не вызывая ПрочитатьXML() для каждого тега.
Посмотрим на реализацию пошагового разбора:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Exchange\data.xml");
ТекущийОбъект = Неопределено;
ИмяТекущегоРеквизита = "";
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяЭлемента = Чтение.Имя;
Если ИмяЭлемента = "CatalogObject.Номенклатура.Вручную" Тогда
ТекущийОбъект = Новый Структура("Ref, IsFolder, Code, Description");
ИначеЕсли ТекущийОбъект <> Неопределено Тогда
ИмяТекущегоРеквизита = ИмяЭлемента;
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.Текст Тогда
Если ТекущийОбъект <> Неопределено И ЗначениеЗаполнено(ИмяТекущегоРеквизита) Тогда
ТекущийОбъект.Вставить(ИмяТекущегоРеквизита, Чтение.Значение);
КонецЕсли;
ИначеЕсли Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
Если Чтение.Имя = "CatalogObject.Номенклатура.Вручную" Тогда
// Обрабатываем и записываем собранный объект
Если ТекущийОбъект <> Неопределено Тогда
Ссылка = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(ТекущийОбъект.Ref));
// Логика создания и заполнения объекта
КонецЕсли;
ТекущийОбъект = Неопределено;
Иначе
ИмяТекущегоРеквизита = "";
КонецЕсли;
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Если требуется использовать стандартный механизм ПрочитатьXML() для десериализации объектов 1С целиком одной командой, структура XML должна строго соответствовать XSD-схеме конфигурации.
Выгрузить схему типов конфигурации можно следующими способами:
ФабрикаXDTO.ЭкспортXMLСхемы(ПространствоИмен).При наличии корректных пространств имен разбор выполняется без пошагового чтения каждого реквизита:
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Exchange\data.xml");
// Переходим к первому объекту
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.ЛокальноеИмя = "CatalogObject.Номенклатура" Тогда
// Десериализуем весь объект справочника одной командой
ОбъектНоменклатура = ПрочитатьXML(Чтение);
ОбъектНоменклатура.Записать();
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
Подведем итог: при разборе произвольных или модифицированных XML-структур не следует смешивать вызовы ЧтениеXML.Прочитать() и ПрочитатьXML() для отдельных дочерних тегов. Для небольших файлов наиболее простым решением является ПостроительDOM, а для больших объемов данных — построчный разбор состояний через ЧтениеXML с проверкой типа узла.