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

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

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

В этой статье мы подробно разберем, как решить эту задачу на платформах 1С v8.2 и v8.3. Мы проанализируем несколько различных подходов — от простых "хитростей" платформы до гибкого манипулирования XML-документом через модель DOM.

Постановка задачи и стандартный механизм выгрузки

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


ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
Хмл = Новый ЗаписьXML();
ЗаписьСообщения.НачатьЗапись(ХМЛ, Узел);
Выборка = ПланыОбмена.ВыбратьИзменения(Узел, НомерСообщения);
Пока Выборка.Следующий() Цикл
    Данные = Выборка.Получить();
    ЗаписатьXML(хмл, Данные);
КонецЦикла;
ЗаписьСообщения.ЗакончитьЗапись();

Метод ЗаписатьXML(хмл, Данные) автоматически преобразует объект Данные (в нашем случае ДокументОбъект) в XML-структуру, соответствующую его метаданным. Наша цель — внедрить в этот XML-пакет свое кастомное поле с произвольным значением, не меняя структуру самого документа в конфигураторе.

Способ 1. Использование экспортных переменных модуля объекта

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

Выясним, как это реализовать по шагам:

  1. Откроем модуль нужного документа (например, документа РеализацияТоваровУслуг).
  2. В самом верху модуля, в разделе объявления переменных, добавим строчку кода:

Перем МойДополнительныйРеквизит Экспорт;

  1. Перед выгрузкой объекта (например, при получении его из выборки плана обмена или перед непосредственной записью) мы можем заполнить эту переменную:

// Получаем объект для выгрузки
ДокументОбъект = Данные.ПолучитьОбъект();
ДокументОбъект.МойДополнительныйРеквизит = "Нужное нам значение";
ЗаписатьXML(хмл, ДокументОбъект);

При выполнении этого кода платформа автоматически добавит в XML-структуру документа новый тег МойДополнительныйРеквизит со значением "Нужное нам значение".

Важные особенности этого метода:

  1. Физически в базе данных этот реквизит не создается и место на диске не занимает.
  2. Если принимающая сторона также построена на платформе 1С и использует стандартный метод ПрочитатьXML, то в конфигурации-приемнике в модуле этого же документа обязательно должна быть объявлена аналогичная экспортная переменная Перем МойДополнительныйРеквизит Экспорт;. В противном случае чтение файла завершится ошибкой десериализации.
  3. Согласно современным стандартам разработки (например, стандартам фирмы "1С" для БСП), использование экспортных переменных в модулях объектов не рекомендуется, так как это усложняет контроль за жизненным циклом данных. Тем не менее, как быстрый "костыль" для нетиповых обменов этот метод работает безотказно.

Способ 2. Модификация структуры через объектную модель DOM

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

Разберем по шагам комбинированный алгоритм работы с DOM:

  1. Выгрузим документ во временную строку XML стандартным методом ЗаписатьXML.
  2. Прочитаем полученную строку в объект ДокументDOM.
  3. Найдем корневой элемент XML-документа и программно добавим в него новый дочерний узел с произвольным именем и значением.
  4. Запишем модифицированный DOM-документ в итоговый файл обмена.

Посмотрим на пример практической реализации этого алгоритма:


// 1. Сериализуем объект во временную строку XML
ЗаписьВременногоXML = Новый ЗаписьXML;
ЗаписьВременногоXML.УстановитьСтроку();
ЗаписатьXML(ЗаписьВременногоXML, Данные);
СтрокаXML = ЗаписьВременногоXML.Закрыть();

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

// 3. Добавляем свой произвольный элемент в структуру DOM
КорневойЭлемент = ДокументDOM.ДокументЭлемент;
НовыйЭлемент = ДокументDOM.СоздатьЭлемент("ИмяМоегоРеквизита");
НовыйЭлемент.ТекстовоеСодержимое = "ЗначениеРеквизита";
КорневойЭлемент.ДобавитьДочерний(НовыйЭлемент);

// 4. Записываем измененный DOM в итоговый поток (хмл)
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, хмл);

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

Способ 3. Комбинированная ручная сериализация

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

Проанализируем пример кода:


Пока Выборка.Следующий() Цикл
    Данные = Выборка.Получить();
    
    Если ТипЗнч(Данные) = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
        ВыгрузитьРеализациюСДопПолями(хмл, Данные);
    Иначе
        ЗаписатьXML(хмл, Данные);
    КонецЕсли;
КонецЦикла;

Внутри процедуры ВыгрузитьРеализациюСДопПолями мы вручную открываем тег документа, записываем его стандартные реквизиты, добавляем свои кастомные узлы и закрываем тег. Это выглядит следующим образом:


Процедура ВыгрузитьРеализациюСДопПолями(ЗаписьXML, ДокументОбъект)
    
    // Открываем основной тег документа
    ЗаписьXML.ЗаписатьНачалоЭлемента("DocumentObject.РеализацияТоваровУслуг");
    
    // Записываем стандартные реквизиты через ЗаписатьXML, указывая имя тега
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Ссылка, "Ref");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.ПометкаУдаления, "DeletionMark");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Дата, "Date");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Номер, "Number");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Проведен, "Posted");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Организация, "Организация");
    ЗаписатьXML(ЗаписьXML, ДокументОбъект.Контрагент, "Контрагент");
    
    // Внедряем наш произвольный реквизит
    ЗаписьXML.ЗаписатьНачалоЭлемента("МойДополнительныйРеквизит");
    ЗаписьXML.ЗаписатьТекст("ЗначениеРеквизита");
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Записываем табличные части (при необходимости)
    // ... здесь может быть цикл по строкам табличных частей ...
    
    // Закрываем основной тег документа
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
КонецПроцедуры

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

Почему не подходит стандартное свойство "ДополнительныеСвойства"?

У начинающих разработчиков часто возникает соблазн использовать для этой задачи стандартное свойство объекта ДополнительныеСвойства, которое имеет тип Структура. Действительно, в эту структуру можно записать любые произвольные данные в оперативной памяти:

ДокументОбъект.ДополнительныеСвойства.Вставить("МойПараметр", "Значение");

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

Современное решение для платформы 1С 8.3: Расширения конфигурации

Если ваша информационная база работает на современной платформе 1С v8.3 (и не использует режим совместимости с устаревшими версиями), то самым правильным, надежным и безопасным решением будет использование Расширений конфигурации.

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

Резюме и рекомендации

Мы рассмотрели несколько способов решения проблемы. Какой из них выбрать, зависит от конкретных условий вашей задачи:

  1. Если вам нужно быстрое решение "здесь и сейчас", базы источника и приемника написаны на 1С, и вы можете вносить изменения в код конфигураций — используйте экспортные переменные модуля объекта (Способ 1).
  2. Если вы интегрируетесь со сторонней системой, схема XML строго зафиксирована, а конфигурацию 1С менять нельзя — используйте обработку через модель DOM (Способ 2).
  3. Если вам требуется полный, точечный контроль над каждым тегом и структурой XML при выгрузке конкретного сложного документа — используйте комбинированную ручную сериализацию (Способ 3).
  4. Если вы работаете на современной платформе 1С 8.3 — всегда отдавайте предпочтение расширениям конфигурации как наиболее технологичному и поддерживаемому методу.
← На главную