При выгрузке данных в формате XML через стандартные средства 1С программисты часто сталкиваются с особенностью: если элемент не содержит вложенных данных (пустой), платформа автоматически схлопывает его в краткую форму <Tag/>. Согласно мировым стандартам XML, записи <Tag></Tag> и <Tag/> абсолютно идентичны и должны корректно обрабатываться любой системой. Однако на практике мы часто встречаем "капризные" принимающие системы или старые государственные шлюзы, которые выдают ошибку, если не видят явного закрывающего тега.
В этой статье мы подробно разберем, почему так происходит, и проанализируем несколько способов решения задачи: от простых манипуляций с текстом до использования объектной модели документа (DOM).
Для работы с XML платформа 1С использует объект ЗаписьXML, который базируется на системных библиотеках операционной системы (обычно это MSXML в Windows или libxml2 в Linux). Эти библиотеки жестко следуют спецификации XML. Оптимизация пустого узла до самозакрывающегося тега — это стандартное поведение. К сожалению, в объекте ПараметрыЗаписиXML не существует свойства типа "ЗапретитьСокращенныеТеги". Поэтому нам придется применять обходные пути.
Важное уточнение по терминологии: в обсуждениях часто путают атрибуты и элементы. Атрибуты — это свойства внутри тега (например, <UNIT type="weight">), и они не могут иметь закрывающих тегов. В нашем случае речь идет исключительно об элементах (узлах) XML-дерева.
Рассмотрим самый "хитрый" и в то же время чистый способ. Чтобы обмануть парсер и заставить его считать, что элемент не пуст, мы можем записать внутрь него пустой комментарий. С точки зрения данных это ничего не меняет (большинство систем игнорируют комментарии), но для ЗаписьXML элемент перестает быть пустым.
Разберем этот метод на примере кода:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\data.xml", "UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("PAYD");
ЗаписьXML.ЗаписатьТекст("test");
ЗаписьXML.ЗаписатьКонецЭлемента(); // Будет <PAYD>test</PAYD>
// Применяем хитрость с комментарием для узла UNIT
ЗаписьXML.ЗаписатьНачалоЭлемента("UNIT");
ЗаписьXML.ЗаписатьКомментарий("");
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.Закрыть();
В результате в файле мы увидим строку: <UNIT><!----></UNIT>. Если принимающая сторона обрабатывает XML стандартным парсером, она просто не заметит комментарий, но увидит желанную структуру тегов.
Если нам нужна максимальная структурная точность, проанализируем работу через DOM-модель. В 1С это объекты ДокументDOM и ЗаписьDOM. В отличие от последовательной записи, DOM позволяет явно создать текстовый узел, даже если он содержит пустую строку, и привязать его к элементу.
Рассмотрим по шагам, как это реализовать:
// Создаем основу документа
ДокументDOM = Новый ДокументDOM;
КорневойУзел = ДокументDOM.СоздатьЭлемент("Data");
ДокументDOM.ДобавитьДочерний(КорневойУзел);
// Создаем элемент UNIT
ЭлементUnit = ДокументDOM.СоздатьЭлемент("UNIT");
// Явно создаем текстовый узел с пустой строкой
ПустойТекст = ДокументDOM.СоздатьТекстовыйУзел("");
ЭлементUnit.ДобавитьДочерний(ПустойТекст);
КорневойУзел.ДобавитьДочерний(ЭлементUnit);
// Записываем результат
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
РезультатСтрокой = ЗаписьXML.Закрыть();
Этот метод более тяжеловесен для системы (потребляет больше памяти), но он наиболее "правильный" с точки зрения программирования логики документа. В некоторых версиях платформы ЗаписьDOM при наличии даже пустого текстового узла принудительно выводит полную форму записи тега.
Если вы столкнулись с тем, что ни один из методов выше не помогает (что бывает при специфических настройках системных библиотек), мы можем прибегнуть к простому и надежному способу — текстовой замене. Мы формируем XML обычным способом, а затем "разворачиваем" сокращенные теги вручную.
Посмотрим на пример реализации:
// 1. Формируем XML в строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
// ... логика записи элементов ...
ЗаписьXML.ЗаписатьНачалоЭлемента("UNIT");
ЗаписьXML.ЗаписатьКонецЭлемента();
ТекстXML = ЗаписьXML.Закрыть();
// 2. Выполняем замену
// Важно: делайте замену только для конкретных тегов, чтобы не испортить структуру
ТекстXML = СтрЗаменить(ТекстXML, "<UNIT/>", "<UNIT></UNIT>");
// 3. Сохраняем в файл или отправляем в API
Важный момент: использование СтрЗаменить оправдано, если вы точно знаете имена тегов, которые нужно развернуть. Массовая замена всех вхождений /> на полные закрывающие теги с помощью регулярных выражений требует осторожности, чтобы не нарушить валидность документа.
Этот метод превращает объект ЗаписьXML в обычный текстовый редактор. Мы можем вручную вставить в поток любую строку. Это дает полный контроль над результатом, но лишает нас преимуществ автоматического экранирования спецсимволов.
Проанализируем ситуацию:
ЗаписьXML.ЗаписатьНачалоЭлемента("ROOT");
// Пишем тег вручную
ЗаписьXML.ЗаписатьБезОбработки("<UNIT></UNIT>");
ЗаписьXML.ЗаписатьКонецЭлемента();
Используйте этот метод только в крайних случаях, так как при записи сложных данных (например, строк с символами & или <) вам придется самостоятельно заботиться об их преобразовании в сущности & и <.
Подведем итоги нашего исследования:
<Tag/> является стандартом.Правильный выбор метода зависит от объема выгружаемого файла и того, насколько критично для вас сохранение "чистоты" XML-кода.