При интеграции 1С со сторонними информационными системами разработчики нередко сталкиваются с жесткими требованиями к структуре выгружаемых XML-документов. Сторонняя система или валидатор на принимающей стороне может требовать «чистый» XML без объявлений пространств имен (таких как xmlns, xmlns:xs, xmlns:xsi). При этом поставщик формата предоставляет схему XSD, в которой не указан атрибут targetNamespace. Попытка загрузить такую схему напрямую в конфигуратор или использовать стандартные средства ФабрикаXDTO приводит к ошибке, так как механизм XDTO в платформе 1С жестко ориентирован на строгую типизацию с обязательной привязкой к URI пространства имен.
Проанализируем ситуацию, выясним внутренние причины этого ограничения платформы и подробно разберем практические способы решения задачи, позволяющие сохранить все удобства работы с объектами XDTO.
Рассмотрим подробнее архитектурные особенности модели XDTO в 1С:Предприятии:
ФабрикаXDTO, должен иметь уникальный непустой URIПространстваИмен. Если схема XSD не содержит targetNamespace, платформа отбрасывает объявленные в ней типы.ФабрикаXDTO.Создать() в качестве первого параметра обязательно принимает тип XDTO (ТипОбъектаXDTO или ТипЗначенияXDTO), получить который без указания пространства имен стандартным путем невозможно.targetNamespace допустимы (их часто называют хамелеон-схемами), однако модель XDTO 1С не поддерживает генерацию XML-документов с полностью отсутствующими пространствами имен.Разберем по шагам наиболее быстрый и распространенный подход, который заключается в добавлении временного пространства имен в XSD-схему для комфортной сериализации объектов через ФабрикаXDTO, с последующим удалением лишних атрибутов xmlns из готового файла.
Шаг 1. Модификация схемы XSD: Добавляем в корневой элемент схемы фиктивный атрибут, например: targetNamespace="http://tempuri.org/export" и загружаем схему в конфигурацию как XDTO-пакет либо динамически через СоздатьФабрикуXDTO().
Шаг 2. Формирование XML-документа: Заполняем объект XDTO привычным способом и выгружаем его в файл с помощью объекта ЗаписьXML.
Шаг 3. Удаление объявлений пространств имен: Посмотрим на пример кода, выполняющего построчную замену с помощью объекта ТекстовыйДокумент:
ИмяФайлаXML = "C:\Exchange\export.xml";
// Запись сформированного XDTO-объекта в файл
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайлаXML, "UTF-8");
ФабрикаXDTO.ЗаписатьXML(Запись, МойОбъектXDTO);
Запись.Закрыть();
// Очистка от объявления фиктивного пространства имен
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ИмяФайлаXML, КодировкаТекста.UTF8);
Для Номер = 1 По Текст.КоличествоСтрок() Цикл
СтрокаТекста = Текст.ПолучитьСтроку(Номер);
СтрокаТекста = СтрЗаменить(СтрокаТекста, " xmlns=""http://tempuri.org/export""", "");
СтрокаТекста = СтрЗаменить(СтрокаТекста, " xmlns:xs=""http://www.w3.org/2001/XMLSchema""", "");
СтрокаТекста = СтрЗаменить(СтрокаТекста, " xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""", "");
Текст.ЗаменитьСтроку(Номер, СтрокаТекста);
КонецЦикла;
Текст.Записать(ИмяФайлаXML, КодировкаТекста.UTF8);
Начиная с версии платформы 1С 8.3.23, для строковых манипуляций доступны функции работы с регулярными выражениями. Это позволяет очистить любые объявления xmlns универсальным шаблоном без жесткой привязки к конкретной строке URI.
Посмотрим на пример реализации через чтение всего текста файла и замену по регулярному выражению:
// Считываем весь XML-файл в строковую переменную
ЧтениеТекста = Новый ЧтениеТекста(ИмяФайлаXML, КодировкаТекста.UTF8);
СодержимоеXML = ЧтениеТекста.Прочитать();
ЧтениеТекста.Закрыть();
// Удаляем все объявления пространств имен вида xmlns="..." и xmlns:префикс="..."
СодержимоеXML = СтрЗаменитьПоРегулярномуВыражению(СодержимоеXML, "\s+xmlns(:[a-zA-Z0-9_-]+)?=""[^""]*""", "");
// Перезаписываем итоговый файл
ЗаписьТекста = Новый ЗаписьТекста(ИмяФайлаXML, КодировкаТекста.UTF8);
ЗаписьТекста.Записать(СодержимоеXML);
ЗаписьТекста.Закрыть();
Важно: При использовании регулярных выражений или простой строковой замены убедитесь, что в самих выгружаемых строковых данных (например, в тексте комментариев или наименований) не встречаются сочетания символов, совпадающие с шаблоном xmlns="...", чтобы случайно не повредить полезные данные.
Если требуется гарантировать 100% синтаксическую корректность XML и исключить риски повреждения строковых данных в тегах, наиболее правильным решением является применение объекта ПреобразованиеXSL. XSLT-шаблон разбирает структуру XML как дерево и корректно переносит все теги и атрибуты без сохранения их пространств имен.
Разберем по шагам выполнение XSLT-трансформации:
ФабрикаXDTO с любым временным пространством имен.local-name(), исключая префиксы и атрибуты xmlns.Посмотрим на пример кода 1С и структуру XSLT:
// Текст XSLT-преобразования для удаления пространств имен
ТекстXSLT =
"<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
| <xsl:output method=""xml"" indent=""yes"" encoding=""UTF-8""/>
| <xsl:template match=""*"">
| <xsl:element name=""{local-name()}"">
| <xsl:apply-templates select=""@* | node()""/>
| </xsl:element>
| </xsl:template>
| <xsl:template match=""@*"">
| <xsl:attribute name=""{local-name()}"">
| <xsl:value-of select="".""/>
| </xsl:attribute>
| </xsl:template>
|</xsl:stylesheet>";
Преобразование = Новый ПреобразованиеXSL;
Преобразование.ЗагрузитьСтроку(ТекстXSLT);
// Выполняем трансформацию исходного файла в чистый результирующий файл
Преобразование.Преобразовать(ИмяИсходногоФайлаXML, ИмяЧистогоФайлаXML);
Если структура файла относительно простая и не содержит глубокой вложенности типов, иногда проще отказаться от XDTO и сформировать XML напрямую низкоуровневыми методами объекта ЗаписьXML. В этом случае программист полностью контролирует наличие любых служебных атрибутов.
Посмотрим на пример ручной записи:
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл("C:\Exchange\direct_export.xml", "UTF-8");
Запись.ЗаписатьОбъявлениеXML();
// Запись корневого элемента без указания URI пространства имен
Запись.ЗаписатьНачалоЭлемента("RootDocument");
Запись.ЗаписатьНачалоЭлемента("Header");
Запись.ЗаписатьАтрибут("Number", "0000123");
Запись.ЗаписатьАтрибут("Date", Формат(ТекущаяДата(), "ДФ=yyyy-MM-dd"));
Запись.ЗаписатьКонецЭлемента(); // Header
Запись.ЗаписатьНачалоЭлемента("Items");
Запись.ЗаписатьНачалоЭлемента("Item");
Запись.ЗаписатьЭлемент("Code", "A-001");
Запись.ЗаписатьЭлемент("Quantity", "10");
Запись.ЗаписатьКонецЭлемента(); // Item
Запись.ЗаписатьКонецЭлемента(); // Items
Запись.ЗаписатьКонецЭлемента(); // RootDocument
Запись.Закрыть();
Хотя ФабрикаXDTO не умеет работать со схемами без пространств имен, платформенный механизм проверки XML отлично с ними справляется. Объект СхемаXML умеет загружать исходную схему без targetNamespace, а объект ЧтениеXML позволяет валидировать итоговый очищенный файл.
Посмотрим на пример валидации:
Схема = Новый СхемаXML;
Схема.РасположениеСхемы = "C:\Schemas\original_schema.xsd";
ПараметрыЧтения = Новый ПараметрыЧтенияXML;
ПараметрыЧтения.Схемы.Добавить(Схема);
ПараметрыЧтения.ПроверятьДействительность = Истина;
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл("C:\Exchange\export.xml", ПараметрыЧтения);
Попытка
Пока Чтение.Прочитать() Цикл
// Считываем весь файл до конца для полной проверки по схеме
КонецЦикла;
Сообщить("XML-документ успешно прошел валидацию!");
Исключение
Сообщить("Ошибка валидации XML: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Чтение.Закрыть();
При необходимости выгрузить XML без пространств имен по XSD-схеме оптимальным подходом является комбинирование инструментов:
ФабрикаXDTO с добавлением фиктивного targetNamespace в схему, после чего очищаем файл с помощью ПреобразованиеXSL или строковой замены.СхемаXML вместе с ЧтениеXML, которые корректно работают с оригинальными схемами без пространств имен.ЗаписьXML.