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

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

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

Анализ проблемы: Стандарт XML vs Реальность

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

Разберем ситуацию со стороны 1С. Платформа при использовании объекта ЗаписьXML по умолчанию оптимизирует документ. Как только вызывается метод ЗаписатьКонецЭлемента(), если внутри не было записано никакого содержимого, система автоматически схлопывает узел до краткой формы. Это поведение зашито в системных библиотеках (MSXML в Windows или libxml2 в Linux), и простого свойства "НеСхлопыватьТеги" у объекта ЗаписьXML не существует.

Решение №1: Использование объекта КаноническаяЗаписьXML

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

Рассмотрим, как изменить привычный код выгрузки, чтобы получить желаемый результат. Вместо стандартной записи мы прогоним сформированный XML через канонизатор.

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


// Сначала создаем XML обычным способом в строку или файл
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("Root");
Запись.ЗаписатьНачалоЭлемента("EmptyTag");
Запись.ЗаписатьКонецЭлемента(); // Здесь 1С по умолчанию создаст <EmptyTag/>
Запись.ЗаписатьКонецЭлемента();
РезультатОбычный = Запись.Закрыть();

// Теперь используем Каноническую запись для преобразования
КаноническаяЗапись = Новый КаноническаяЗаписьXML;
КаноническаяЗапись.УстановитьСтроку();
// Чтение может быть из строки, файла или потока
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(РезультатОбычный);
КаноническаяЗапись.Записать(ЧтениеXML);
ИтоговыйXML = КаноническаяЗапись.Закрыть();

В переменной ИтоговыйXML мы получим структуру, где вместо сокращенного тега будет стоять полная пара <EmptyTag></EmptyTag>. Этот метод гарантирует стопроцентную совместимость с любыми, даже самыми капризными парсерами.

Решение №2: Принудительная запись пустой строки

Если по каким-то причинам использование КаноническаяЗаписьXML неудобно (например, при очень больших объемах данных, где важна скорость), можно применить "хитрость". Мы можем обмануть парсер 1С, заставив его думать, что у тега есть содержимое, даже если это содержимое — пустая строка.

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

  1. Открываем начало элемента с помощью ЗаписатьНачалоЭлемента().
  2. Вместо немедленного закрытия вызываем метод ЗаписатьТекст("").
  3. Закрываем элемент.

Пример реализации:


ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьXML.ЗаписатьНачалоЭлемента("Товар");
ЗаписьXML.ЗаписатьНачалоЭлемента("Наименование");
ЗаписьXML.ЗаписатьТекст("Кирпич красный");
ЗаписьXML.ЗаписатьКонецЭлемента();

ЗаписьXML.ЗаписатьНачалоЭлемента("Описание");
// Принудительно записываем пустую строку, чтобы предотвратить самозакрытие
ЗаписьXML.ЗаписатьТекст(""); 
ЗаписьXML.ЗаписатьКонецЭлемента();

ЗаписьXML.ЗаписатьКонецЭлемента();
Результат = ЗаписьXML.Закрыть();

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

Решение №3: Работа через DOM (Документ DOM)

Еще один надежный способ управления структурой XML — использование объектной модели документа (DOM). При выгрузке через ЗаписьDOM у нас появляется чуть больше контроля над тем, как сериализуется дерево объектов.

Разберем пример создания документа через DOM:


ДокументDOM = Новый ДокументDOM;
КорневойУзел = ДокументDOM.СоздатьЭлемент("Data");
ДокументDOM.ДобавитьДочерний(КорневойУзел);

ПустойЭлемент = ДокументDOM.СоздатьЭлемент("EmptyNode");
// Добавляем текстовый узел с пустой строкой
ТекстПусто = ДокументDOM.СоздатьТекстовыйУзел("");
ПустойЭлемент.ДобавитьДочерний(ТекстПусто);
КорневойУзел.ДобавитьДочерний(ПустойЭлемент);

ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
Результат = ЗаписьXML.Закрыть();

Использование ДокументDOM позволяет явно указать, что у элемента есть дочерний узел (пусть и текстовый пустой), что предотвращает формирование самозакрывающегося тега при записи в поток.

Как аргументировать позицию перед разработчиками CMS

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

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

← На главную