Как сформировать пустой XML-тег с закрывающим элементом вместо самозакрывающегося в 1С?

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

При интеграции 1С с внешними системами (например, при выгрузке файлов для банков, в частности, формата Сбербанка) разработчики часто сталкиваются с жесткими требованиями к синтаксису XML. По спецификации W3C XML 1.0 формы записи <Индекс /> и <Индекс></Индекс> семантически абсолютно эквивалентны. Однако сторонние приемные модули могут использовать упрощенные или устаревшие парсеры, которые выдают ошибку при обработке самозакрывающегося тега и требуют обязательного наличия парного закрывающего тега или тега с пробельным символом.

Разберем причины такого поведения платформы «1С:Предприятие» и рассмотрим практические способы решения этой задачи.

Почему 1С сворачивает пустые теги

Проанализируем стандартное поведение объекта платформы ЗаписьXML. При потоковой записи или сериализации DOM-модели, если между вызовами методов ЗаписатьНачалоЭлемента и ЗаписатьКонецЭлемента не было выведено никакого содержимого (или была передана пустая строка), встроенный сериализатор автоматически оптимизирует XML и выводит компактную форму <Индекс />.

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

Способ 1. Использование объекта КаноническаяЗаписьXML

В платформе 1С предусмотрен объект КаноническаяЗаписьXML, который формирует документ в строгом соответствии со стандартом Canonical XML (c14n). Согласно этому стандарту, пустые элементы обязательно выводятся с открывающим и закрывающим тегами.

Посмотрим на пример формирования такого документа:


ЗаписьXML = Новый КаноническаяЗаписьXML;
ЗаписьXML.УстановитьСтроку();

ЗаписьXML.ЗаписатьНачалоЭлемента("МестоРождения");
ЗаписьXML.ЗаписатьНачалоЭлемента("Индекс");
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьКонецЭлемента();

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

В результате работы данного кода мы получим XML следующей структуры без самозакрывающихся тегов:

<МестоРождения><Индекс></Индекс></МестоРождения>

Способ 2. Вывод через метод ЗаписатьБезОбработки

Если в вашем коде уже используется стандартный объект ЗаписьXML и переход на каноническую запись нежелателен, можно воспользоваться методом ЗаписатьБезОбработки (WriteRaw). Этот метод выводит переданную строку байт в байт без участия механизма нормализации тегов.

Разберем по шагам реализацию данного подхода при проверке значения на заполненность:


ЗаписьXML.ЗаписатьНачалоЭлемента("МестоРождения");

Если ПустаяСтрока(ЗначениеИндекс) Тогда
    // Выводим тег напрямую в требуемом виде
    ЗаписьXML.ЗаписатьБезОбработки("<Индекс></Индекс>");
Иначе
    ЗаписьXML.ЗаписатьНачалоЭлемента("Индекс");
    ЗаписьXML.ЗаписатьТекст(Строка(ЗначениеИндекс));
    ЗаписьXML.ЗаписатьКонецЭлемента();
КонецЕсли;

ЗаписьXML.ЗаписатьКонецЭлемента();

Способ 3. Постобработка XML-строки

Еще один надежный способ — получить итоговый текст XML-файла и выполнить строковую замену перед сохранением документа на диск или отправкой по API.

Посмотрим на пример реализации:

  1. Формируем XML-документ стандартным способом в память через ЗаписьXML.УстановитьСтроку().
  2. Получаем результирующую строку с помощью метода Закрыть().
  3. Заменяем свернутый тег на требуемый формат с помощью функции СтрЗаменить.
  4. Сохраняем результат через объект ЗаписьТекста.

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

// Если принимающая система ожидает тег без пробела:
ТекстXML = СтрЗаменить(ТекстXML, "<Индекс />", "<Индекс></Индекс>");
ТекстXML = СтрЗаменить(ТекстXML, "<Индекс/>", "<Индекс></Индекс>");

// Если формат банка строго требует тег с пробелом внутри:
// ТекстXML = СтрЗаменить(ТекстXML, "<Индекс />", "<Индекс> </Индекс>");

ЗаписьФайла = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8);
ЗаписьФайла.Записать(ТекстXML);
ЗаписьФайла.Закрыть();

Способ 4. Добавление пробельного символа или неразрывного пробела

Если принимающая сторона интерпретирует тег <Индекс> </Индекс> именно как содержащий символ пробела (или неразрывный пробел Символы.НПП), достаточно передать данный символ в значение текстового узла:


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

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

← На главную