Как правильно выгрузить данные в XML в 1С: пишем элементы и значения вместо атрибутов

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

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

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

---

Постановка задачи и анализ типичной ошибки

Рассмотрим ситуацию, с которой столкнулся автор темы. Исходный код выглядел следующим образом:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлуДляЗаписи);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("complexes");
ЗаписьXML.ЗаписатьНачалоЭлемента("complex");
ЗаписьXML.ЗаписатьАтрибут("id", "1122");
ЗаписьXML.ЗаписатьАтрибут("name", "Комплекс №1");
ЗаписьXML.ЗаписатьАтрибут("address", "город, улица, дом");
ЗаписьXML.ЗаписатьКонецЭлемента(); // complex
ЗаписьXML.ЗаписатьКонецЭлемента(); // complexes

В результате выполнения этого кода формируется XML-структура, где все параметры объекта complex записаны внутри самого тега в виде атрибутов:


<?xml version="1.0" encoding="UTF-8"?>
<complexes>
    <complex id="1122" name="Комплекс №1" address="город, улица, дом">
    </complex>
</complexes>

Однако внешняя система-приемник (загрузчик) ожидает совершенно иной формат, где каждый параметр является полноценным вложенным XML-элементом (тегом) со своим текстовым значением:


<complexes>
    <complex>
        <id>1122</id>
        <name>Комплекс №1</name>
        <address>город, улица, дом</address>
    </complex>
</complexes>

Выясним причину, почему так произошло. Автор кода использовал метод ЗаписатьАтрибут(), который добавляет свойства внутрь открывающего тега. Для того чтобы получить вложенную структуру, необходимо открывать новый элемент для каждого свойства, записывать в него текст и закрывать его.

---

Решение проблемы с помощью объекта ЗаписьXML

Для того чтобы преобразовать атрибуты во вложенные элементы, нам понадобится метод ЗаписатьТекст(). Этот метод записывает текстовое содержимое в текущий активный узел XML-документа.

Разберем по шагам правильный алгоритм формирования вложенной структуры:

  1. Открываем основной элемент-контейнер: ЗаписатьНачалоЭлемента("complexes").
  2. Открываем элемент карточки объекта: ЗаписатьНачалоЭлемента("complex").
  3. Для каждого реквизита (например, id) открываем свой подчиненный элемент: ЗаписатьНачалоЭлемента("id").
  4. Записываем текстовое значение этого реквизита: ЗаписатьТекст("1122").
  5. Обязательно закрываем подчиненный элемент: ЗаписатьКонецЭлемента().
  6. Повторяем процедуру для всех остальных реквизитов.
  7. Закрываем родительские элементы в обратном порядке.

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


ПутьКФайлуДляЗаписи = "C:\temp\complexes.xml";
ЗаписьXML = Новый ЗаписьXML;

// Настройки записи для создания красивых отступов (форматирования)
ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина, Символы.Таб);
ЗаписьXML.ОткрытьФайл(ПутьКФайлуДляЗаписи, ПараметрыЗаписиXML);

ЗаписьXML.ЗаписатьОбъявлениеXML();

// Открываем корневой элемент
ЗаписьXML.ЗаписатьНачалоЭлемента("complexes");

// Начало выгрузки конкретного комплекса
ЗаписьXML.ЗаписатьНачалоЭлемента("complex");

// Запись элемента "id"
ЗаписьXML.ЗаписатьНачалоЭлемента("id");
ЗаписьXML.ЗаписатьТекст("1122");
ЗаписьXML.ЗаписатьКонецЭлемента(); // id

// Запись элемента "name"
ЗаписьXML.ЗаписатьНачалоЭлемента("name");
ЗаписьXML.ЗаписатьТекст("Комплекс №1");
ЗаписьXML.ЗаписатьКонецЭлемента(); // name

// Запись элемента "address"
ЗаписьXML.ЗаписатьНачалоЭлемента("address");
ЗаписьXML.ЗаписатьТекст("город, улица, дом");
ЗаписьXML.ЗаписатьКонецЭлемента(); // address

ЗаписьXML.ЗаписатьКонецЭлемента(); // complex (закрываем карточку)
ЗаписьXML.ЗаписатьКонецЭлемента(); // complexes (закрываем корень)

ЗаписьXML.Закрыть();

Важный совет: Обратите внимание на использование объекта ПараметрыЗаписиXML. Передав в метод ОткрытьФайл() параметры с третьим аргументом Истина и символом табуляции, мы заставляем платформу автоматически расставлять переносы строк и отступы. Это делает XML-файл легко читаемым человеком.

---

Выгрузка множественных (повторяющихся) тегов

В сообщении №15 автор столкнулся с дополнительным требованием: для одного комплекса необходимо выгрузить список телефонных номеров, причем теги должны называться одинаково и идти друг за другом:


<complex>
    <id>1122</id>
    <tel>городской</tel>
    <tel>сотовый</tel>
</complex>

Объект ЗаписьXML работает по принципу последовательного потока (SAX-like writer). Ему абсолютно все равно, сколько одинаковых тегов вы запишете подряд. Главное — соблюдать строгую вложенность и своевременно закрывать каждый элемент перед открытием следующего.

Ошибки у разработчиков обычно возникают из-за неправильной подготовки данных в коде 1С. Например, если попытаться упаковать данные выгрузки в объект Структура:


// ТАК ДЕЛАТЬ НЕЛЬЗЯ для повторяющихся полей!
ДанныеКомплекса = Новый Структура;
ДанныеКомплекса.Вставить("id", "1122");
ДанныеКомплекса.Вставить("tel", "городской");
ДанныеКомплекса.Вставить("tel", "сотовый"); // Предыдущее значение перезапишется!

Структура 1С не может содержать ключи с одинаковыми именами. Чтобы обойти это ограничение, списки однотипных данных необходимо хранить в объектах Массив или ТаблицаЗначений. Проанализируем правильный шаблон кода для выгрузки структуры с повторяющимися тегами:


// Подготовим демонстрационные данные
ДанныеКомплекса = Новый Структура;
ДанныеКомплекса.Вставить("Идентификатор", "1122");
ДанныеКомплекса.Вставить("Наименование", "Комплекс №1");

СписокТелефонов = Новый Массив;
СписокТелефонов.Добавить("8-495-000-00-00"); // городской
СписокТелефонов.Добавить("8-900-000-00-00"); // сотовый
ДанныеКомплекса.Вставить("Телефоны", СписокТелефонов);

// Алгоритм выгрузки в XML
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\complexes.xml", Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина));

ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("complexes");
ЗаписьXML.ЗаписатьНачалоЭлемента("complex");

// Записываем простые реквизиты
ЗаписьXML.ЗаписатьНачалоЭлемента("id");
ЗаписьXML.ЗаписатьТекст(ДанныеКомплекса.Идентификатор);
ЗаписьXML.ЗаписатьКонецЭлемента();

ЗаписьXML.ЗаписатьНачалоЭлемента("name");
ЗаписьXML.ЗаписатьТекст(ДанныеКомплекса.Наименование);
ЗаписьXML.ЗаписатьКонецЭлемента();

// Записываем массив телефонов через цикл
Для Каждого НомерТелефона Из ДанныеКомплекса.Телефоны Цикл
    ЗаписьXML.ЗаписатьНачалоЭлемента("tel");
    ЗаписьXML.ЗаписатьТекст(НомерТелефона);
    ЗаписьXML.ЗаписатьКонецЭлемента(); // tel
КонецЦикла;

ЗаписьXML.ЗаписатьКонецЭлемента(); // complex
ЗаписьXML.ЗаписатьКонецЭлемента(); // complexes
ЗаписьXML.Закрыть();

---

Альтернативный вариант: Использование объектной модели XDTO

В финальных сообщениях форума автору справедливо советуют обратить внимание на механизм XDTO (XML Data Transfer Objects). При работе с большими и сложными структурами данных ручная сборка через ЗаписьXML становится трудоемкой и подверженной ошибкам (легко забыть закрыть тег или перепутать их последовательность).

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

Рассмотрим, как решить нашу задачу с помощью фабрики XDTO, если у нас нет готовой схемы (XSD):


// 1. Создаем модель типов динамически (или используем готовую XSD-схему из метаданных)
// Для простых задач можно использовать глобальную фабрику XDTO, если типы стандартные.
// Покажем пример работы с динамическим созданием объектов через фабрику.

// Допустим, у нас в конфигурации импортирована схема данных. 
// Создадим объект по типу из схемы:
ТипКомплекса = ФабрикаXDTO.Тип("http://www.example.org/complexes", "ComplexType");
НовыйКомплекс = ФабрикаXDTO.Создать(ТипКомплекса);

НовыйКомплекс.id = "1122";
НовыйКомплекс.name = "Комплекс №1";
НовыйКомплекс.address = "город, улица, дом";

// Если свойство "tel" в схеме описано как список (MaxOccurs = unbounded),
// мы можем просто добавлять в него элементы:
НовыйКомплекс.tel.Добавить("8-495-000-00-00");
НовыйКомплекс.tel.Добавить("8-900-000-00-00");

// 2. Сериализация объекта в файл XML
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\complex_xdto.xml", Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина));

ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, НовыйКомплекс, "complex", "http://www.example.org/complexes");

ЗаписьXML.Закрыть();

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

---

Альтернативный вариант: Использование DOM (Document Object Model)

Если вам необходимо выгружать документы, структура которых постоянно меняется, или требуется дописывать новые узлы в уже существующие ветки готового файла в произвольном порядке, потоковый объект ЗаписьXML не подойдет, так как он умеет писать только строго вперед.

В этом случае на помощь приходит DOM-модель. Она позволяет построить дерево документа прямо в оперативной памяти компьютера.

Рассмотрим пример генерации нашей структуры через DOM:


// Создаем документ DOM
ДокументDOM = Новый ДокументDOM;

// Создаем корневой элемент
КорневойЭлемент = ДокументDOM.СоздатьЭлемент("complexes");
ДокументDOM.ЗаписатьОбластьДанных(КорневойЭлемент); // Делаем его корневым

// Создаем элемент "complex"
ЭлементComplex = ДокументDOM.СоздатьЭлемент("complex");
КорневойЭлемент.ДобавитьДочерний(ЭлементComplex);

// Функция-помощник для быстрого добавления вложенного текстового узла
ЭлементID = ДокументDOM.СоздатьЭлемент("id");
ТекстID = ДокументDOM.СоздатьТекст(Строка("1122"));
ЭлементID.ДобавитьДочерний(ТекстID);
ЭлементComplex.ДобавитьДочерний(ЭлементID);

// Аналогично добавляем имя
ЭлементName = ДокументDOM.СоздатьЭлемент("name");
ТекстName = ДокументDOM.СоздатьТекст("Комплекс №1");
ЭлементName.ДобавитьДочерний(ТекстName);
ЭлементComplex.ДобавитьДочерний(ЭлементName);

// Запись всего DOM-дерева в файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\complex_dom.xml");
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
ЗаписьXML.Закрыть();

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

---

Подведение итогов

Для решения классической задачи выгрузки данных во внешние системы программисту 1С важно четко понимать различия в структуре XML-документа. Если принимающей стороне нужны вложенные теги с текстовым содержимым, используйте последовательную связку методов ЗаписатьНачалоЭлемента(), ЗаписатьТекст() и ЗаписатьКонецЭлемента().

Если формат выгрузки часто меняется или строго регламентирован внешней схемой XSD — делайте выбор в пользу XDTO. Это сэкономит ваше время на ручном написании кода и убережет от ошибок форматирования.

← На главную