Как правильно копировать XML-элементы в 1С с выборочным фильтром?

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

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

Рассмотрим различные подходы к решению этой задачи в 1С, их преимущества, недостатки и особенности применения.

1. Последовательное чтение и запись с помощью ЧтениеXML и ЗаписьXML

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

Принцип работы

  1. Мы используем объект ЧтениеXML для пошагового чтения узлов исходного XML-документа.
  2. Одновременно мы используем объект ЗаписьXML для формирования нового XML-документа.
  3. При чтении каждого узла мы проверяем его ТипУзла и ИмяУзла. Если узел соответствует нашим условиям выборочного копирования, мы записываем его в новый документ, включая его атрибуты и вложенные элементы.
  4. Если узел не соответствует условиям, мы можем его пропустить с помощью метода Пропустить(), чтобы не обрабатывать его дочерние элементы.

Преимущества

Недостатки

Пример выборочного копирования (без элемента Transporter)

Предположим, у нас есть исходный XML:


XML1 ="
<Documents>
 <Document>
  <Supplier ID="1"/>
  <Buyer ID="2"/>
  <Transporter ID="3"/>
 </Document>
</Documents>";

И мы хотим получить:


<Documents>
 <Document>
  <Supplier ID="1"/>
  <Buyer ID="2"/>
 </Document>
</Documents>

Вот как мы можем реализовать это с помощью ЧтениеXML и ЗаписьXML:


ИсходныйXMLСтрока = XML1;
ЦелевойXMLСтрока = "";

// Создаем объекты для чтения и записи XML
ЧтениеИсходногоXML = Новый ЧтениеXML();
ЧтениеИсходногоXML.УстановитьСтроку(ИсходныйXMLСтрока);

ЗаписьЦелевогоXML = Новый ЗаписьXML();
ЗаписьЦелевогоXML.УстановитьСтроку(ЦелевойXMLСтрока);
ЗаписьЦелевогоXML.ЗаписатьОбъявлениеXML(); // Записываем объявление XML

// Обходим исходный XML
Пока ЧтениеИсходногоXML.Прочитать() Цикл

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

            // Обходим дочерние элементы "Documents" (в данном случае - "Document")
            Пока ЧтениеИсходногоXML.Прочитать() И ЧтениеИсходногоXML.ТипУзла <> ТипУзлаXML.КонецЭлемента И ЧтениеИсходногоXML.ИмяУзла <> "Documents" Цикл
                Если ЧтениеИсходногоXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
                    Если ЧтениеИсходногоXML.ИмяУзла = "Document" Тогда
                        ЗаписьЦелевогоXML.ЗаписатьНачалоЭлемента(ЧтениеИсходногоXML.ИмяУзла);
                        // Копируем атрибуты элемента "Document", если есть
                        Для Каждого Атрибут Из ЧтениеИсходногоXML.Атрибуты Цикл
                            ЗаписьЦелевогоXML.ЗаписатьАтрибут(Атрибут.Имя, Атрибут.Значение);
                        КонецЦикла;

                        // Обходим дочерние элементы "Document"
                        Пока ЧтениеИсходногоXML.Прочитать() И ЧтениеИсходногоXML.ТипУзла <> ТипУзлаXML.КонецЭлемента И ЧтениеИсходногоXML.ИмяУзла <> "Document" Цикл
                            Если ЧтениеИсходногоXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
                                // Выборочное копирование: копируем "Supplier" и "Buyer", пропускаем "Transporter"
                                Если ЧтениеИсходногоXML.ИмяУзла = "Supplier" ИЛИ ЧтениеИсходногоXML.ИмяУзла = "Buyer" Тогда
                                    ЗаписьЦелевогоXML.ЗаписатьНачалоЭлемента(ЧтениеИсходногоXML.ИмяУзла);
                                    // Копируем атрибуты элемента (например, ID)
                                    Для Каждого Атрибут Из ЧтениеИсходногоXML.Атрибуты Цикл
                                        ЗаписьЦелевогоXML.ЗаписатьАтрибут(Атрибут.Имя, Атрибут.Значение);
                                    КонецЦикла;
                                    ЗаписьЦелевогоXML.ЗаписатьКонецЭлемента(); // Закрываем скопированный элемент
                                Иначе
                                    // Пропускаем элемент "Transporter" и другие, если они не нужны
                                    ЧтениеИсходногоXML.Пропустить();
                                КонецЕсли;
                            КонецЕсли;
                        КонецЦикла;
                        ЗаписьЦелевогоXML.ЗаписатьКонецЭлемента(); // Закрываем "Document"
                    Иначе
                        // Пропускаем другие элементы под "Documents", если они не "Document"
                        ЧтениеИсходногоXML.Пропустить();
                    КонецЕсли;
                КонецЕсли;
            КонецЦикла;
            ЗаписьЦелевогоXML.ЗаписатьКонецЭлемента(); // Закрываем "Documents"
        Иначе
            // Пропускаем любые другие корневые элементы, если они не "Documents"
            ЧтениеИсходногоXML.Пропустить();
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

ЧтениеИсходногоXML.Закрыть();
РезультатXMLСтрока = ЗаписьЦелевогоXML.Закрыть();

Сообщить(РезультатXMLСтрока);

2. DOM (Document Object Model)

DOM представляет XML-документ в виде древовидной структуры объектов в памяти. Это удобный способ работы с XML, когда размер файла не является критическим.

Принцип работы

  1. Мы используем объект ПостроительDOM для считывания всего XML-файла и построения его объектной модели в оперативной памяти.
  2. После создания DOM-модели мы можем обращаться к узлам документа как к объектам, используя методы для поиска (например, ПолучитьЭлементыПоИмени), чтения, изменения и удаления элементов.
  3. Для выборочного копирования мы обходим узлы исходного DOM-дерева, выбираем нужные элементы и создаем новый DOM-документ или записываем выбранные элементы в новый XML-файл.

Преимущества

Недостатки

Пример выборочного копирования с использованием DOM

Для нашего примера с выборочным копированием Supplier и Buyer без Transporter, мы можем использовать DOM следующим образом:


ИсходныйXMLСтрока = XML1;
ЦелевойXMLСтрока = "";

// 1. Создаем построитель DOM и читаем исходный XML
Построитель = Новый ПостроительDOM;
ДокументИсходныйXML = Построитель.Прочитать(Новый ЧтениеXML().УстановитьСтроку(ИсходныйXMLСтрока));

// 2. Создаем новый DOM-документ для результата
ДокументЦелевойXML = Построитель.СоздатьДокумент();

// 3. Создаем корневой элемент "Documents" для целевого документа
КорневойЭлементЦелевой = ДокументЦелевойXML.СоздатьЭлемент("Documents");
ДокументЦелевойXML.ДобавитьДочерний(КорневойЭлементЦелевой);

// 4. Находим элемент "Document" в исходном XML
СписокДокументов = ДокументИсходныйXML.ПолучитьЭлементыПоИмени("Document");

Если СписокДокументов.Количество() > 0 Тогда
    ЭлементИсходныйДокумент = СписокДокументов.Получить(0); // Берем первый найденный "Document"

    // 5. Создаем элемент "Document" для целевого документа
    ЭлементЦелевойДокумент = ДокументЦелевойXML.СоздатьЭлемент("Document");
    КорневойЭлементЦелевой.ДобавитьДочерний(ЭлементЦелевойДокумент);

    // 6. Обходим дочерние узлы исходного элемента "Document"
    Для Каждого ДочернийУзел Из ЭлементИсходныйДокумент.ДочерниеУзлы Цикл
        // Проверяем, является ли узел элементом и имеет ли нужное имя
        Если ДочернийУзел.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            Если ДочернийУзел.ИмяУзла = "Supplier" ИЛИ ДочернийУзел.ИмяУзла = "Buyer" Тогда
                // 7. Создаем новый элемент в целевом документе
                НовыйДочернийЭлемент = ДокументЦелевойXML.СоздатьЭлемент(ДочернийУзел.ИмяУзла);

                // 8. Копируем все атрибуты из исходного элемента в новый
                Для Каждого Атрибут Из ДочернийУзел.Атрибуты Цикл
                    НовыйДочернийЭлемент.УстановитьАтрибут(Атрибут.Имя, Атрибут.Значение);
                КонецЦикла;

                // 9. Добавляем новый элемент в целевой "Document"
                ЭлементЦелевойДокумент.ДобавитьДочерний(НовыйДочернийЭлемент);
            КонецЕсли;
            // Элемент "Transporter" будет пропущен, так как не соответствует условию
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

// 10. Записываем получившийся DOM-документ в строку
ЗаписьЦелевогоXML = Новый ЗаписьXML();
ЗаписьЦелевогоXML.УстановитьСтроку(ЦелевойXMLСтрока);
ДокументЦелевойXML.Записать(ЗаписьЦелевогоXML);
РезультатXMLСтрока = ЗаписьЦелевойXML.Закрыть();

Сообщить(РезультатXMLСтрока);

3. XSLT-преобразования (eXtensible Stylesheet Language Transformations)

XSLT - это мощный язык для преобразования XML-документов, который позволяет трансформировать один XML-формат в другой. Это очень эффективный способ для выборочного копирования и реструктуризации XML, особенно когда логику преобразования желательно отделить от основного кода 1С.

Принцип работы

  1. Мы создаем таблицу стилей XSLT (XSL-файл или строковое представление), которая содержит правила преобразования. Эти правила определяют, какие элементы исходного документа следует выбрать (с помощью XPath-выражений), как их обработать и как представить в выходном XML-документе.
  2. В 1С мы используем объект ПреобразованиеXSL для применения этой таблицы стилей к исходному XML.
  3. XSLT-процессор выполняет преобразование, и мы получаем новый XML-документ согласно заданным правилам.

Преимущества

Недостатки

Пример XSLT-преобразования

Сначала определим нашу XSLT-таблицу стилей. Она будет выглядеть так:


// XSLT-таблица стилей (можно сохранить в файл и загружать)
// В данном примере она встроена в строку
ТекстXSLT = "<?xml version=""1.0"" encoding=""UTF-8""?>
|<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
|    <xsl:output method=""xml"" indent=""yes""/>
|
|    <!-- Главный шаблон: соответствует корневому элементу Documents -->
|    <xsl:template match=""/Documents"">
|        <Documents>
|            <!-- Применяем шаблоны к дочерним элементам Document -->
|            <xsl:apply-templates select=""Document""/>
|        </Documents>
|    </xsl:template>
|
|    <!-- Шаблон для элемента Document -->
|    <xsl:template match=""Document"">
|        <Document>
|            <!-- Копируем элемент Supplier со всеми его атрибутами и содержимым -->
|            <xsl:copy-of select=""Supplier""/>
|            <!-- Копируем элемент Buyer со всеми его атрибутами и содержимым -->
|            <xsl:copy-of select=""Buyer""/>
|            <!-- Элемент Transporter здесь не упоминается и, следовательно, будет пропущен -->
|        </Document>
|    </xsl:template>
|
|    <!-- Шаблон по умолчанию для всех остальных узлов, чтобы они не копировались -->
|    <xsl:template match=""text()|@*|node()""/>
|
|</xsl:stylesheet>";

Теперь применим эту таблицу стилей в 1С:


ИсходныйXMLСтрока = XML1;
ЦелевойXMLСтрока = "";

// Создаем объект ПреобразованиеXSL
Преобразователь = Новый ПреобразованиеXSL();

// Устанавливаем исходный XML-документ
ЧтениеИсходногоXML = Новый ЧтениеXML();
ЧтениеИсходногоXML.УстановитьСтроку(ИсходныйXMLСтрока);
Преобразователь.УстановитьИсходныйДокумент(ЧтениеИсходногоXML);

// Устанавливаем таблицу стилей XSLT
ЧтениеXSLT = Новый ЧтениеXML();
ЧтениеXSLT.УстановитьСтроку(ТекстXSLT);
Преобразователь.УстановитьТаблицуСтилей(ЧтениеXSLT);

// Выполняем преобразование
РезультатXMLСтрока = Преобразователь.Преобразовать();

// Закрываем потоки чтения
ЧтениеИсходногоXML.Закрыть();
ЧтениеXSLT.Закрыть();

Сообщить(РезультатXMLСтрока);

4. XDTO (XML Data Transfer Objects)

XDTO - это механизм 1С для обмена данными посредством XML, который позволяет оперировать не непосредственно узлами XML, а прикладными понятиями (объектами), определенными в XDTO-пакетах (схемах). Если у вас есть четко определенные XDTO-пакеты для исходной и целевой структуры XML, этот подход может быть очень удобным.

Принцип работы

  1. Сначала необходимо иметь XDTO-пакеты, которые описывают структуру XML-документа. Эти пакеты могут быть импортированы из XML-схем (XSD).
  2. Используется объект ФабрикаXDTO для создания объектов на основе описанных в пакетах типов.
  3. Метод ФабрикаXDTO.ПрочитатьXML() считывает XML-данные и преобразует их в набор XDTO-объектов, соответствующих определенной схеме.
  4. После этого мы работаем с этими XDTO-объектами как с обычными объектами 1С: копируем нужные свойства, изменяем их и формируем новый набор XDTO-объектов, который затем записываем обратно в XML с помощью ФабрикаXDTO.ЗаписатьXML().

Преимущества

Недостатки

Особое внимание к повторному чтению ЧтениеXML

Важный момент, на который мы обратили внимание в сообщениях форума (Сообщение 8 и 9): объект ЧтениеXML является потоковым. Это означает, что после того, как вы прочитали из него данные (например, с помощью ПостроительDOM.Прочитать(ЧтениеXML) или ФабрикаXDTO.ПрочитатьXML(ЧтениеXML)), указатель чтения перемещается в конец документа. Если вы попытаетесь прочитать из того же объекта ЧтениеXML повторно, он вернет пустые данные или вызовет ошибку, так как читать больше нечего.

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


ИсходныйXMLСтрока = XML1;

// *** Пример ошибки (как в Сообщении 8) ***
// ЧтениеXML_Поток = Новый ЧтениеXML();
// ЧтениеXML_Поток.УстановитьСтроку(ИсходныйXMLСтрока);
// 
// Построитель_DOM = Новый ПостроительDOM;
// ДокументXML_DOM = Построитель_DOM.Прочитать(ЧтениеXML_Поток); // Указатель ЧтениеXML_Поток теперь в конце
// 
// // Следующая строка вызовет ошибку или прочитает ничего,
// // потому что ЧтениеXML_Поток уже прочитан до конца
// ДанныеXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML_Поток); 


// *** Правильный подход: каждый раз создаем новый объект ЧтениеXML ***

// 1. Использование DOM (например)
ЧтениеXML_ДляDOM = Новый ЧтениеXML();
ЧтениеXML_ДляDOM.УстановитьСтроку(ИсходныйXMLСтрока);

Построитель_DOM = Новый ПостроительDOM;
ДокументXML_DOM = Построитель_DOM.Прочитать(ЧтениеXML_ДляDOM);
ЧтениеXML_ДляDOM.Закрыть(); // Важно закрыть поток после использования

// 2. Использование XDTO после DOM (или в любой другой раз)
//    Для XDTO нам потребуется определить XDTO-пакет, описывающий структуру XML.
//    Предположим, у нас есть пакет "ПримерXML" и тип "DocumentsType".

// Пример определения XDTO-пакета (только для иллюстрации)
// В реальной ситуации вы бы загружали пакет из файла или метаданных
// ФабрикаXDTO.ЗагрузитьПакет("ПримерXML");
// ТипДокументовXDTO = ФабрикаXDTO.ПолучитьТип("http://www.example.com", "DocumentsType");

// Создаем новый объект ЧтениеXML для XDTO
ЧтениеXML_ДляXDTO = Новый ЧтениеXML();
ЧтениеXML_ДляXDTO.УстановитьСтроку(ИсходныйXMLСтрока);

// Если бы у нас был XDTO-пакет, мы бы прочитали так:
// ИсходныеДокументыXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML_ДляXDTO, ТипДокументовXDTO);
// ЧтениеXML_ДляXDTO.Закрыть();

// **Концептуальный пример выборочного копирования с XDTO:**
// Предположим, что ИсходныеДокументыXDTO содержит структуру:
// ИсходныеДокументыXDTO.Document[0].Supplier.ID
// ИсходныеДокументыXDTO.Document[0].Buyer.ID
// ИсходныеДокументыXDTO.Document[0].Transporter.ID

// Создадим объект для целевых документов
// ЦелевыеДокументыXDTO = ФабрикаXDTO.Создать(ТипДокументовXDTO);

// Для Каждого ТекущийДокументXDTO Из ИсходныеДокументыXDTO.Document Цикл
//     НовыйДокументXDTO = ФабрикаXDTO.Создать(ТипДокументовXDTO.Свойства.Получить("Document").Тип); // Тип для элемента Document
//     НовыйДокументXDTO.Supplier = ТекущийДокументXDTO.Supplier; // Копируем Supplier
//     НовыйДокументXDTO.Buyer = ТекущийДокументXDTO.Buyer;       // Копируем Buyer
//     // Элемент Transporter просто не копируем
//     ЦелевыеДокументыXDTO.Document.Добавить(НовыйДокументXDTO);
// КонецЦикла;

// Запись полученных XDTO-объектов в XML
// ЗаписьЦелевогоXML = Новый ЗаписьXML();
// ЗаписьЦелевогоXML.УстановитьСтроку("");
// ФабрикаXDTO.ЗаписатьXML(ЗаписьЦелевогоXML, ЦелевыеДокументыXDTO);
// РезультатXMLСтрокаXDTO = ЗаписьЦелевогоXML.Закрыть();

// Сообщить("Результат XDTO: " + РезультатXMLСтрокаXDTO);
Сообщить("Пример XDTO показан концептуально, т.к. требует предварительного определения XDTO-пакетов.");

В этом примере мы видим, что для работы с XDTO требуется не только код 1С, но и заранее определенные XDTO-пакеты, описывающие структуру XML. Это делает XDTO мощным, но более сложным инструментом для ad-hoc задач.

Общие рекомендации при работе с XML в 1С

При выборе метода и оптимизации работы с XML-файлами, особенно большими, мы рекомендуем учитывать следующие моменты:

  1. Размер файла: Это самый критичный фактор. Для файлов до 50-100 МБ DOM может быть достаточно удобным. Для файлов свыше 100 МБ или при ограниченных ресурсах предпочтительнее использовать потоковую обработку (ЧтениеXML / ЗаписьXML). XSLT может быть эффективен для любых размеров, так как его реализация часто оптимизирована для работы с потоками, но в 1С это зависит от конкретной реализации объекта ПреобразованиеXSL.
  2. Сложность преобразований: Если требуется сложная логика фильтрации, переименования узлов, изменения структуры или агрегации данных, XSLT является наиболее подходящим инструментом. Для простых выборочных копирований DOM или потоковая обработка могут быть быстрее в разработке.
  3. 64-битный сервер 1С:Предприятие: При работе с большими объемами данных в памяти (например, при использовании DOM для больших XML) переход на 64-битный сервер 1С:Предприятие может значительно помочь, так как он позволяет процессу 1С использовать более 4 ГБ оперативной памяти.
  4. Разделение выгрузки: Если возможно, рассмотрите стратегию разделения больших XML-файлов на несколько меньших частей. Это снижает пиковую нагрузку на память и упрощает обработку.
  5. Кэширование и индексация: Если данные для XML-документа извлекаются из базы данных, оптимизация запросов, использование индексов и кэширование данных могут значительно ускорить процесс подготовки XML.

В заключение, мы выяснили, что в 1С существует несколько эффективных способов выборочного копирования XML-элементов. Выбор конкретного метода должен основываться на размере обрабатываемых файлов, сложности требуемых преобразований и наличии предопределенных схем данных. Каждый из подходов – будь то потоковая обработка, DOM, XSLT или XDTO – имеет свои сильные стороны и оптимальные сценарии применения.

← На главную