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

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

В процессе обмена данными между различными системами, например, при выгрузке из "Мой склад" и загрузке в "1С:Бухгалтерия", часто возникает необходимость предварительно обработать файл обмена. Задача усложняется, если целевая база находится в облачном сервисе "1С:Фреш", где возможности доработки ограничены. Рассмотрим, как можно модифицировать XML-файл формата Enterprise Data, не загружая его целиком в память, изменить нужные поля и сохранить результат для последующей загрузки.

Проблема заключается в том, что файл может быть очень большим, и стандартные методы, такие как полная загрузка в DOM-модель, могут привести к чрезмерному потреблению оперативной памяти. Требуется найти оптимальный способ, который позволит внести изменения (например, поправить номера документов или формат сумм) и при этом будет производительным.

Анализ подходов к обработке XML в 1С

Прежде чем перейти к практическому решению, проанализируем основные методы работы с XML, доступные на платформе 1С, и их применимость к нашей задаче.

  1. Последовательное чтение/запись (ЧтениеXML, ЗаписьXML). Этот подход является самым эффективным с точки зрения использования памяти. Файл читается узел за узлом, что позволяет обрабатывать файлы практически неограниченного размера. Мы можем читать данные из одного файла, модифицировать их "на лету" и сразу записывать в другой. Основной недостаток — сложность кода и необходимость вручную контролировать структуру XML, чтобы не нарушить её валидность. Именно этот метод мы и разберем подробно.
  2. Объектная модель документа (DOM). Этот метод предполагает полную загрузку XML в память в виде дерева объектов. Он значительно упрощает навигацию по элементам и их изменение, так как мы получаем полный доступ ко всей структуре документа. Основной недостаток — высокое потребление памяти, что делает его непригодным для больших файлов.
  3. Механизм XDTO (XML Data Transfer Objects). Это наиболее современный и рекомендуемый 1С способ работы с XML, структура которых заранее известна и описана (например, через XSD-схему). Формат Enterprise Data как раз основан на XDTO. Этот механизм позволяет работать с XML-данными как с типизированными объектами 1С, что делает код более надежным и читаемым. XDTO автоматически контролирует типы данных, обязательные поля и пространства имен. Это "правильный" путь, но он может показаться более сложным для разовых задач, требующих быстрой модификации файла "сбоку".

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

Практическое решение: обработка XML через последовательный разбор и сборку

Рассмотрим реализацию внешней обработки, которая читает исходный XML-файл, разбирает его в древовидную структуру в памяти (массив структур), позволяет внести изменения, а затем собирает новый XML-файл из измененной структуры.

Шаг 1. Структура обработки и чтение файла

Создадим обработку с формой, на которой будет поле для выбора исходного файла и кнопка для запуска обработки. Основная логика будет выполняться на клиенте, так как работа с файлами там наиболее удобна.

Основная процедура-обработчик будет выглядеть следующим образом: она открывает файл для чтения, вызывает функцию разбора, а затем вызывает функцию сборки, записывая результат в новый файл с суффиксом `_res`.


&НаКлиенте
Процедура ОбработатьФайл(Команда)
    
    Ч = Новый ЧтениеXML();
    Ч.ОткрытьФайл(ВыбИмяФайла);
    КодировкаИсточника = Ч.КодировкаИсточника;
    
    // Разбираем XML в массив структур
    Строки = РазобратьXML(Ч);
    Ч.Закрыть();
    
    // Здесь будет логика модификации данных в массиве "Строки"
    
    Ф = Новый Файл(ВыбИмяФайла);
    РезИмяФайла = Ф.Путь + Ф.ИмяБезРасширения + "_res" + Ф.Расширение;
    
    З = Новый ЗаписьXML;
    З.ОткрытьФайл(РезИмяФайла, КодировкаИсточника);
    З.ЗаписатьОбъявлениеXML();
    
    // Собираем XML из массива структур
    СобратьXML(З, Строки);
    З.Закрыть();
    
КонецПроцедуры

Шаг 2. Рекурсивная функция разбора XML

Для разбора XML в удобную структуру напишем рекурсивную функцию РазобратьXML. Она будет обходить все узлы и формировать массив структур. Каждая структура в массиве представляет собой XML-узел и содержит его имя (тег), атрибуты, текстовое содержимое и вложенные узлы (рекурсивный вызов).


&НаКлиенте
Функция РазобратьXML(Ч, Строки = Неопределено, ТекСтрока = Неопределено) Экспорт
    Если Строки = Неопределено Тогда
        Строки = Новый Массив();
    КонецЕсли;
    
    Пока Ч.Прочитать() Цикл
        Если Ч.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            // Создаем структуру для нового элемента
            ТекСтрока = Новый Структура("Тег, Тип, Тело", Ч.Имя);
            ТекСтрока.Вставить("Атрибуты", Новый Соответствие());
            ТекСтрока.Вставить("Строки", Новый Массив());
            Строки.Добавить(ТекСтрока);
            
            // Читаем все атрибуты элемента
            Если Ч.КоличествоАтрибутов() > 0 Тогда
                Пока Ч.ПрочитатьАтрибут() Цикл
                    ТекСтрока.Атрибуты.Вставить(Ч.Имя, Ч.Значение);
                КонецЦикла
            КонецЕсли;
            
            // Рекурсивно вызываем разбор для дочерних элементов
            РазобратьXML(Ч, ТекСтрока.Строки, ТекСтрока);
            
        ИначеЕсли Ч.ТипУзла = ТипУзлаXML.Текст Тогда
            // Записываем текстовое содержимое тега
            ТекСтрока.Тело = Ч.Значение;
            
        ИначеЕсли Ч.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
            // При закрытии тега выходим из рекурсии на уровень выше
            Возврат Неопределено;
            
        ИначеЕсли Ч.ТипУзла = ТипУзлаXML.ИнструкцияОбработки Тогда
            ТекСтрока.Тело = Ч.Значение;
            
        КонецЕсли;
    КонецЦикла;
    
    Возврат Строки;
КонецФункции

Шаг 3. Рекурсивная функция сборки XML

Функция СобратьXML выполняет обратную операцию. Она рекурсивно обходит нашу структуру данных (массив структур) и с помощью объекта ЗаписьXML формирует итоговый файл.


&НаКлиенте
Функция СобратьXML(З, Строки)
    Для Каждого ТекСтрока ИЗ Строки Цикл
        Если ТекСтрока.Тип = Неопределено Тогда
            // Записываем начало элемента
            З.ЗаписатьНачалоЭлемента(ТекСтрока.Тег);
            
            // Записываем все атрибуты
            Для Каждого ЭлАтрибут ИЗ ТекСтрока.Атрибуты Цикл
                З.ЗаписатьАтрибут(ЭлАтрибут.Ключ, ЭлАтрибут.Значение);
            КонецЦикла;
            
            // Записываем текстовое содержимое
            Если ТекСтрока.Тело <> Неопределено Тогда
                З.ЗаписатьТекст(ТекСтрока.Тело);
            КонецЕсли;
            
            // Рекурсивно собираем дочерние элементы
            СобратьXML(З, ТекСтрока.Строки);
            
            // Записываем конец элемента
            З.ЗаписатьКонецЭлемента();
        КонецЕсли;
    КонецЦикла;
КонецФункции

Шаг 4. Модификация данных

Теперь, когда у нас есть функции для разбора и сборки, мы можем вставить логику обработки между ними. Например, нам нужно исправить формат сумм, которые выгружаются из "Мой склад" с избыточной точностью.

Создадим процедуру, которая будет обходить полученное дерево и вносить изменения. Вот пример кода, который форматирует значения в тегах, связанных с суммами, до двух знаков после запятой:


// Этот код нужно вставить в процедуру ОбработатьФайл
// после вызова РазобратьXML(Ч)

// ...
Строки = РазобратьXML(Ч);
Ч.Закрыть();

// Вызываем обработку данных
ОбработатьДанные(Строки);

Ф = Новый Файл(ВыбИмяФайла);
// ...

// ...
&НаКлиенте
Процедура ОбработатьДанные(Строки)
    Для каждого Строка Из Строки Цикл
        Тег = Строка.Тег;
        
        // Обрабатываем неправильный формат сумм
        Если Тег = "СуммаВозврата" ИЛИ Тег = "СуммаНДС" ИЛИ Тег = "Себестоимость" Тогда
            Значение = Строка.Тело;
            Если ЗначениеЗаполнено(Значение) Тогда
                Поз = Найти(Значение, ".");
                Если Поз <> 0 И Поз < СтрДлина(Значение) - 2 Тогда
                    НовоеЗначение = Формат(Число(Значение), "ЧДЦ=2; ЧРД=.; ЧН=0; ЧГ=");
                    Строка.Тело = НовоеЗначение;
                    Сообщить("Изменено значение тега: " + Тег + " с: " + Значение + " на: " + НовоеЗначение);
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        
        // Рекурсивный вызов для дочерних элементов
        Если Строка.Строки.Количество() > 0 Тогда
            ОбработатьДанные(Строка.Строки);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Важные нюансы при ручной обработке XML

Хотя предложенный метод работает, важно помнить о его "подводных камнях".

В заключение, ручной разбор и сборка XML — это мощный инструмент для решения задач быстрой модификации файлов, особенно в условиях ограничений, как с "1С:Фреш". Он обеспечивает высокую производительность при работе с большими объемами данных. Однако для построения надежных и долгосрочных интеграционных решений всегда стоит в первую очередь рассматривать использование платформенного механизма XDTO, который обеспечивает строгий контроль над структурой и типами данных.

← На главную