При интеграции 1С со сторонними информационными системами часто возникает жесткое требование к структуре XML-документа: корневой элемент не должен содержать служебных атрибутов пространств имен. Однако при использовании механизма XDTO платформа 1С автоматически добавляет в корневой узел объявления схем.
Разберем подробнее, почему возникает данная особенность, и рассмотрим практические способы ее решения.
Проанализируем архитектуру механизма XDTO в платформе 1С. Механизм строго ориентирован на спецификацию XML Schema (XSD). Любой пакет XDTO привязан к целевому пространству имен (Target Namespace). Когда мы вызываем метод ФабрикаXDTO.ЗаписатьXML(), платформа обязана гарантировать корректность сериализации типов данных.
В момент записи корневого тега сериализатор 1С принудительно генерирует следующие атрибуты:
xmlns — целевое пространство имен схемы пакета;xmlns:xs="http://www.w3.org/2001/XMLSchema" — пространство имен стандартных типов XML Schema;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" — пространство имен для передачи метаданных экземпляра (например, xsi:type или xsi:nil).Важный момент: во встроенном языке 1С у объектов ФабрикаXDTO и ЗаписьXML нет стандартных параметров или флагов, позволяющих отключить генерацию этих атрибутов. Параметр НазначениеТипаXML.Неявное убирает типы у вложенных элементов, но не очищает сам корневой тег. Если принимающая сторона требует строго «чистый» тег вида <ROOT>, разработчику приходится обходить это ограничение платформы.
Рассмотрим решение, предложенное автором темы. Оно заключается в формировании промежуточного файла через XDTO с последующей построчной перезаписью и заменой корневого тега.
Посмотрим на практическую реализацию этого метода:
Процедура ЗаписатьФайл(ИмяФайла, Данные)
ПолноеИмяФайлаВывода = ПолучитьПуть() + ИмяФайла + ".xml";
ИмяПредварительногоФайла = ПолучитьПуть() + ИмяФайла + "._xml";
// Проверяем соответствие структуры объекта XDTO схеме
Данные.Проверить();
// 1. Формируем первичный XML с помощью XDTO во временный файл
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяПредварительногоФайла, "UTF-8");
Запись.ЗаписатьОбъявлениеXML();
Фабрика.ЗаписатьXML(Запись, Данные, "ROOT");
Запись.Закрыть();
// 2. Построчно читаем временный файл и заменяем открывающий тег ROOT
ЗаписьТ = Новый ЗаписьТекста(ПолноеИмяФайлаВывода, КодировкаТекста.UTF8);
ЧтениеТ = Новый ЧтениеТекста(ИмяПредварительногоФайла, КодировкаТекста.UTF8);
СтрокаФайла = ЧтениеТ.ПрочитатьСтроку();
Пока СтрокаФайла <> Неопределено Цикл
Если СокрЛ(Лев(СтрокаФайла, 5)) = "<ROOT" Тогда
ЗаписьТ.ЗаписатьСтроку("<ROOT>");
Иначе
ЗаписьТ.ЗаписатьСтроку(СтрокаФайла);
КонецЕсли;
СтрокаФайла = ЧтениеТ.ПрочитатьСтроку();
КонецЦикла;
ЧтениеТ.Закрыть();
ЗаписьТ.Закрыть();
// 3. Удаляем временный файл
Попытка
УдалитьФайлы(ИмяПредварительногоФайла);
Исключение
// Обработка ошибки удаления временного файла
КонецПопытки;
КонецПроцедуры
Этот способ прост и надежен для файлов небольшого и среднего размера, когда корневой тег гарантированно занимает одну строку в начале документа.
Выясним, как избежать создания временных файлов на диске. Объект ЗаписьXML умеет выгружать данные напрямую в строковую переменную через метод УстановитьСтроку(). Это ускоряет работу алгоритма и исключает проблемы с правами доступа к файловой системе.
Разберем по шагам алгоритм работы с памятью:
// Сериализуем XDTO в память
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
Запись.ЗаписатьОбъявлениеXML();
ФабрикаXDTO.ЗаписатьXML(Запись, ДанныеXDTO, "ROOT");
СтрокаXML = Запись.Закрыть();
// Находим границы открывающего тега ROOT
ПозицияНачала = СтрНайти(СтрокаXML, "<ROOT");
Если ПозицияНачала > 0 Тогда
ПозицияКонца = СтрНайти(СтрокаXML, ">",, ПозицияНачала);
Если ПозицияКонца > 0 Тогда
// Заменяем блок атрибутов тега ROOT на чистый тег
СтрокаXML = Лев(СтрокаXML, ПозицияНачала - 1) + "<ROOT>" + Сред(СтрокаXML, ПозицияКонца + 1);
КонецЕсли;
КонецЕсли;
// Сохраняем итоговую строку в целевой файл
ЗаписьТекста = Новый ЗаписьТекста(ПолноеИмяФайла, КодировкаТекста.UTF8);
ЗаписьТекста.Записать(СтрокаXML);
ЗаписьТекста.Закрыть();
Если требуется строго следовать стандартам работы с XML и исключить манипуляции со строками, воспользуемся моделью DOM. Платформа 1С предоставляет для этого объекты ПостроительDOM, ДокументDOM и ЗаписьDOM.
Рассмотрим, как удалить атрибуты пространств имен программно через DOM:
// Считываем сформированный XML в объектную модель DOM
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаСАтрибутами);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(ЧтениеXML);
ЧтениеXML.Закрыть();
// Получаем корневой элемент и удаляем нежелательные атрибуты
КорневойЭлемент = ДокументDOM.ЭлементДокумента;
Если КорневойЭлемент.ЕстьАтрибуты() Тогда
// Удаляем стандартные пространства имен схемы
КорневойЭлемент.УдалитьАтрибут("xmlns");
КорневойЭлемент.УдалитьАтрибут("xmlns:xs");
КорневойЭлемент.УдалитьАтрибут("xmlns:xsi");
КонецЕсли;
// Записываем очищенный документ обратно в файл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИтоговоеИмяФайла, "UTF-8");
ЗаписьDOM = Новый ЗаписьDOM;
ЗаписьDOM.Записать(ДокументDOM, ЗаписьXML);
ЗаписьXML.Закрыть();
Проанализируем альтернативный архитектурный подход: если формат выгрузки строго фиксирован и не требует сложной валидации по XSD-схеме, имеет смысл отказаться от пакета XDTO в пользу прямого использования ЗаписьXML.
При использовании методов ЗаписатьНачалоЭлемента(), ЗаписатьАтрибут() и ЗаписатьКонецЭлемента() платформа добавляет в результирующий XML исключительно те теги и атрибуты, которые вы указали явно в коде 1С, полностью исключая служебные пространства имен.