Как правильно и быстро загрузить данные из XML-файла в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Загрузка данных из внешних XML-файлов — одна из самых частых задач при организации обменов между 1С и сторонними системами. Однако при обработке сложных и объемных XML-структур (например, накладных с шапкой и детальными табличными частями товаров) разработчики часто сталкиваются с двумя главными проблемами: неверным считыванием значений параметров (когда одно поле перезаписывает другое) и крайне низкой скоростью работы алгоритма на больших файлах.

В этой статье мы подробно проанализируем причины возникновения ошибки перезаписи реквизитов при последовательном чтении XML, рассмотрим переход на ФабрикаXDTO для многократного ускорения загрузки, а также разберем метод потокового чтения, который позволяет безопасно обрабатывать файлы размером в сотни мегабайт без переполнения оперативной памяти.

Разбор ошибки перезаписи реквизитов при обходе через ЧтениеXML

Рассмотрим распространенную ситуацию. Разработчик пытается прочитать 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 (тип узла Текст) считываются на разных итерациях цикла:

  1. На первой итерации встречается открывающий тег <Date>. Платформа фиксирует ТипУзлаXML.НачалоЭлемента, и переменной Имя присваивается строка "Date".
  2. На следующей итерации считывается содержимое тега (тип Текст). Программа сохраняет его в структуру по ключу Имя.
  3. Если между тегами в XML-файле присутствуют переносы строк, пробелы или промежуточные узлы (например, <City>), переменная Имя без явного сброса сохраняет свое предыдущее значение. При попадании на любой следующий текстовый узел платформа снова выполняет метод Массив.Вставить(Имя, ...) и затирает ранее полученную дату новым значением.

Для корректной работы с простым низкоуровневым чтением следует использовать метод ЧтениеXML.ПрочитатьСодержимоеXDTO() или явно отслеживать закрывающие теги ТипУзлаXML.КонецЭлемента.

Ускорение обработки через ФабрикаXDTO

Если структура файла известна или совпадает с типом приёмника, эффективнее использовать механизм 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 полностью помещает его дерево элементов в оперативную память. На системах с ограниченными ресурсами это может вызвать ошибку "Недостаточно памяти".

Комбинированное потоковое чтение больших 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.Закрыть();

Этот метод гарантирует, что в оперативной памяти одновременно находится только один элемент, а скорость считывания остается максимально возможной.

Особенности формата DATAPACKET и причины зацикливания узлов

При выгрузках из внешних систем (например, на базе Delphi / ClientDataSet) файлы часто имеют формат DATAPACKET. При работе с ними разработчики могут столкнуться с тем, что отдельные узлы (например, CurrencyRate или Line) пропускаются или зацикливают чтение.

Разберем основные причины и методы исправления таких проблем:

  1. Атрибутивная модель DATAPACKET: В формате DATAPACKET данные строк хранится не в дочерних тегах, а в атрибутах узлов <ROW> (например, <ROW AwbNum="123" Date="2023-01-01"/>). При чтении через ЧтениеXML необходимо запрашивать значения с помощью методов ЧтениеXML.ПолучитьАтрибут("ИмяАтрибута") или обходить КоличествоАтрибутов().
  2. Отсутствие свойства коллекции в XSD-схеме: Если вы подключаете XSD-схему к XDTO и считываете повторяющиеся строки Line, убедитесь, что у данного элемента в схеме указан параметр maxOccurs="unbounded". Если парсер XDTO ожидает один элемент, а получает массив, алгоритм может сбиться или уйти в бесконечную обработку.
  3. Нестрогий порядок элементов: Если в схеме XDTO задан строго последовательный тип <xs:sequence>, а в исходном XML порядок тегов нарушен или отсутствуют необязательные поля, считывание данных остановится на расхождении. В таком случае в схеме лучше использовать секцию <xs:all> или снимать жесткую привязку к типам.

Дополнительные советы по ускорению загрузки

Посмотрим на практические рекомендации, которые позволяют выжать максимум производительности при загрузке данных в 1С:

  1. Используйте групповые транзакции: Не записывайте каждый документ отдельной транзакцией. Объединяйте запись пачками (например, по 200–500 документов в одной транзакции с помощью методов НачатьТранзакцию() и ЗафиксироватьТранзакцию()). Это ускорит запись в базу данных в 3–5 раз.
  2. Отключайте лишние обработчики: Если при записи документа запускаются тяжелые подписки на события или автоматические расчеты, при массовой загрузке их стоит временно отключать с помощью параметров обмена или проведения в фоновом режиме.
  3. Двоичный XML (FastInfoSet): При обмене между системами на платформе 1С используйте объекты ЧтениеFastInfoSet и ЗаписьFastInfoSet. Этот бинарный формат читается значительно быстрее стандартного текстового XML.
← На главную