Загрузка данных из внешних XML-файлов — одна из самых частых задач при организации обменов между 1С и сторонними системами. Однако при обработке сложных и объемных XML-структур (например, накладных с шапкой и детальными табличными частями товаров) разработчики часто сталкиваются с двумя главными проблемами: неверным считыванием значений параметров (когда одно поле перезаписывает другое) и крайне низкой скоростью работы алгоритма на больших файлах.
В этой статье мы подробно проанализируем причины возникновения ошибки перезаписи реквизитов при последовательном чтении XML, рассмотрим переход на ФабрикаXDTO для многократного ускорения загрузки, а также разберем метод потокового чтения, который позволяет безопасно обрабатывать файлы размером в сотни мегабайт без переполнения оперативной памяти.
Рассмотрим распространенную ситуацию. Разработчик пытается прочитать XML-файл в цикле с помощью объекта ЧтениеXML, используя конструкцию последовательного перехода по узлам:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\test\test.xml");
Имя = ЧтениеXML.Имя;
Массив = Новый Структура;
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.Имя = "Date" И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Имя = ЧтениеXML.Имя;
ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
Массив.Вставить(Имя, ЧтениеXML.Значение);
КонецЕсли;
Если ЧтениеXML.Имя = "AwbNum" И ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
Имя = ЧтениеXML.Имя;
ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
Массив.Вставить(Имя, ЧтениеXML.Значение);
КонецЕсли;
КонецЦикла;
При таком подходе часто возникает неприятный баг: значение параметра Date перезаписывается совершенно другим реквизитом (например, City). Выясним причину этого поведения.
Объект ЧтениеXML работает как последовательный поток. На каждой итерации цикла Пока ЧтениеXML.Прочитать() Цикл вы перемещаетесь на один узел файла. При этом открывающий тег <Date> (тип узла НачалоЭлемента) и текстовое содержимое 2023-10-25 (тип узла Текст) считываются на разных итерациях цикла:
<Date>. Платформа фиксирует ТипУзлаXML.НачалоЭлемента, и переменной Имя присваивается строка "Date".Текст). Программа сохраняет его в структуру по ключу Имя.<City>), переменная Имя без явного сброса сохраняет свое предыдущее значение. При попадании на любой следующий текстовый узел платформа снова выполняет метод Массив.Вставить(Имя, ...) и затирает ранее полученную дату новым значением.Для корректной работы с простым низкоуровневым чтением следует использовать метод ЧтениеXML.ПрочитатьСодержимоеXDTO() или явно отслеживать закрывающие теги ТипУзлаXML.КонецЭлемента.
Если структура файла известна или совпадает с типом приёмника, эффективнее использовать механизм XDTO. Метод ФабрикаXDTO.ПрочитатьXML() выполняет считывание файла с помощью внутренней C++ реализации платформы, что работает в разы быстрее скриптовых циклов 1С.
Проанализируем пример кода для быстрого чтения всего документа через XDTO:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\test\test.xml");
// Считываем весь XML сразу в объект XDTO
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Теперь можно обращаться к свойствам как к реквизитам объекта
ДатаДокумента = ОбъектXDTO.Date;
НомерНакладной = ОбъектXDTO.AwbNum;
ЧтениеXML.Закрыть();
На практике такой подход позволяет сократить время чтения большого файла (около 500 МБ) с 35–40 минут до 7–8 минут. Однако у этого метода есть существенное ограничение: загрузка полгигабайтного файла целиком через ПрочитатьXML полностью помещает его дерево элементов в оперативную память. На системах с ограниченными ресурсами это может вызвать ошибку "Недостаточно памяти".
Чтобы объединить высокую скорость XDTO и низкое потребление памяти, применим **комбинированный подход**. Мы будем последовательно перемещаться по файлу с помощью ЧтениеXML до ключевого элемента (например, каждой отдельной накладной или строки), и считывать в XDTO только этот конкретный компактный фрагмент.
Рассмотрим пример потоковой обработки файла неограниченного объема:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\test\huge_data.xml");
// Потоковый обход файла
Пока ЧтениеXML.Прочитать() Цикл
// Ищем начало каждого документа
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.ЛокальноеИмя = "Invoice" Тогда
// Считываем в XDTO только один текущий документ
НакладнаяXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
// Создаем и заполняем документ в базе 1С
НовыйДокумент = Документы.ПриходнаяНакладная.СоздатьДокумент();
НовыйДокумент.Дата = НакладнаяXDTO.Date;
НовыйДокумент.Номер = НакладнаяXDTO.AwbNum;
// Обходим строки табличной части
Для Каждого СтрокаТовара Из НакладнаяXDTO.Lines Цикл
НоваяСтрока = НовыйДокумент.Товары.Добавить();
НоваяСтрока.Номенклатура = СтрокаТовара.Item;
НоваяСтрока.Количество = СтрокаТовара.Quantity;
КонецЦикла;
НовыйДокумент.Записать();
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Этот метод гарантирует, что в оперативной памяти одновременно находится только один элемент, а скорость считывания остается максимально возможной.
При выгрузках из внешних систем (например, на базе Delphi / ClientDataSet) файлы часто имеют формат DATAPACKET. При работе с ними разработчики могут столкнуться с тем, что отдельные узлы (например, CurrencyRate или Line) пропускаются или зацикливают чтение.
Разберем основные причины и методы исправления таких проблем:
DATAPACKET данные строк хранится не в дочерних тегах, а в атрибутах узлов <ROW> (например, <ROW AwbNum="123" Date="2023-01-01"/>). При чтении через ЧтениеXML необходимо запрашивать значения с помощью методов ЧтениеXML.ПолучитьАтрибут("ИмяАтрибута") или обходить КоличествоАтрибутов().Line, убедитесь, что у данного элемента в схеме указан параметр maxOccurs="unbounded". Если парсер XDTO ожидает один элемент, а получает массив, алгоритм может сбиться или уйти в бесконечную обработку.<xs:sequence>, а в исходном XML порядок тегов нарушен или отсутствуют необязательные поля, считывание данных остановится на расхождении. В таком случае в схеме лучше использовать секцию <xs:all> или снимать жесткую привязку к типам.Посмотрим на практические рекомендации, которые позволяют выжать максимум производительности при загрузке данных в 1С:
НачатьТранзакцию() и ЗафиксироватьТранзакцию()). Это ускорит запись в базу данных в 3–5 раз.ЧтениеFastInfoSet и ЗаписьFastInfoSet. Этот бинарный формат читается значительно быстрее стандартного текстового XML.