При работе с внешними системами через формат XML разработчики 1С часто сталкиваются с тем, что ФабрикаXDTO принудительно добавляет в генерируемый файл описания пространств имен (атрибуты xmlns, xmlns:xs, xmlns:xsi). Это поведение соответствует стандартам XML-сериализации, однако многие сторонние сервисы, использующие самописные парсеры, не умеют корректно обрабатывать эти атрибуты и отклоняют запросы. В этой статье мы подробно разберем несколько способов, как сделать XML «чистым», сохранив при этом его структуру.
Когда мы создаем объект XDTO на основании типа, определенного в пакете с указанным URI пространства имен, механизм 1С обязан пометить этот узел соответствующим идентификатором. Рассмотрим типовой код, который приводит к появлению лишних данных:
custXml = Новый ЗаписьXML;
custXml.УстановитьСтроку();
ТипCustomerDetail = ФабрикаXDTO.Тип("http://www.eee.ru/score", "typeCustomerDetail");
ОбъектCD = ФабрикаXDTO.Создать(ТипCustomerDetail);
ОбъектCD.lastName = "Иванов1";
ФабрикаXDTO.ЗаписатьXML(custXml, ОбъектCD, "customerDetails");
Результат = custXml.Закрыть();
На выходе мы получим узел, перегруженный служебной информацией. Если принимающая сторона ожидает простой тег customerDetails, нам необходимо вмешаться в процесс формирования файла. Рассмотрим основные методы решения задачи — от простых «костылей» до профессиональных инструментов.
Проанализируем самый быстрый и очевидный вариант — работу с результатом как с обычным текстом. Если XML небольшой, мы можем просто вырезать ненужные подстроки. Это решение часто критикуют за неэлегантность, но оно работает «здесь и сейчас».
Алгоритм действий:
СтрЗаменить для удаления объявлений пространств имен.
// Получаем строку XML
ТекстXML = custXml.Закрыть();
// Удаляем конкретное пространство имен
ТекстXML = СтрЗаменить(ТекстXML, "xmlns=""http://www.eee.ru/score""", "");
ТекстXML = СтрЗаменить(ТекстXML, "xmlns:xs=""http://www.w3.org/2001/XMLSchema""", "");
ТекстXML = СтрЗаменить(ТекстXML, "xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""", "");
// Важно: могут остаться двойные пробелы или некорректно закрытые скобки тега
ТекстXML = СтрЗаменить(ТекстXML, " >", ">");
ТекстXML = СтрЗаменить(ТекстXML, " >", ">");
Плюсы: Простота реализации. Минусы: Риск случайно заменить полезные данные внутри тегов, если они совпадают с URI пространства имен. Метод не подходит для очень больших файлов из-за ограничений памяти на работу со строками.
Выясним, как сделать очистку более надежной и производительной. Мы можем использовать последовательное чтение исходного «грязного» XML и запись его в новый объект ЗаписьXML, игнорируя данные о пространствах имен. Этот метод идеально подходит для больших файлов, так как данные обрабатываются порциями. При необходимости автоматизировать процесс редактирования структуры документа можно использовать внешнюю обработку для перезаписи и модификации XML-файлов.
Рассмотрим пример реализации функции очистки:
Функция ОчиститьXMLОтПространств(ИсходныйТекст)
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ИсходныйТекст);
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
Пока Чтение.Прочитать() Цикл
ТипУзла = Чтение.ТипУзла;
Если ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
// Записываем только локальное имя без URI
Запись.ЗаписатьНачалоЭлемента(Чтение.ЛокальноеИмя);
// Переносим атрибуты, если они не являются объявлениями xmlns
Пока Чтение.ПрочитатьАтрибут() Цикл
Если Чтение.Префикс <> "xmlns" И Чтение.ЛокальноеИмя <> "xmlns" Тогда
Запись.ЗаписатьАтрибут(Чтение.ЛокальноеИмя, Чтение.Значение);
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипУзла = ТипУзлаXML.Текст Тогда
Запись.ЗаписатьТекст(Чтение.Значение);
ИначеЕсли ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
Запись.ЗаписатьКонецЭлемента();
КонецЕсли;
КонецЦикла;
Возврат Запись.Закрыть();
КонецФункции
Этот подход гарантирует, что структура документа не будет нарушена, а все данные внутри тегов останутся в целости.
Проанализируем наиболее «взрослый» и гибкий метод — применение XSL-шаблона. XSLT специально создан для трансформации XML-документов. Мы можем написать крошечный скрипт, который просто копирует элементы, отбрасывая пространства имен. Для расширения возможностей этой технологии можно использовать методику визуализации и преобразования данных через XSLT.
Разберем по шагам создание такого преобразования:
ПреобразованиеXSL.
ТекстXSL =
"<xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
| <xsl:output method=""xml"" indent=""yes"" omit-xml-declaration=""yes""/>
| <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;
Преобразование.ЗагрузитьИзСтроки(ТекстXSL);
// ИсходныйXML - это объект ЧтениеXML или строка
РезультатТрансформации = Преобразование.ПреобразоватьИзСтроки(ГрязныйXML);
Важный момент: Использование local-name() в шаблоне позволяет игнорировать префиксы и URI, создавая абсолютно «плоский» XML. Это самый надежный способ, если структура вложений очень сложная и включает десятки разных пакетов XDTO.
Выясним, можно ли предотвратить появление xmlns еще на этапе генерации. В 1С ФабрикаXDTO ориентируется на метаданные пакета. Если в свойстве пакета «Пространство имен» (Target Namespace) указано значение, оно всегда будет попадать в XML.
Рассмотрим альтернативу:
При использовании такого пакета метод ФабрикаXDTO.ЗаписатьXML(Запись, Объект) увидит, что у объекта нет привязки к URI, и запишет «чистые» теги без атрибутов. Однако помните, что это потребует ручного перекладывания данных из ваших основных объектов в объекты этого «пустого» пакета.
Если объем данных позволяет загрузить весь документ в память, можно воспользоваться объектной моделью DOM. Это позволяет точечно удалять атрибуты узлов.
Проанализируем пример кода:
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(ГрязныйXML);
ПостроительDOM = Новый ПостроительDOM;
ДокументDOM = ПостроительDOM.Прочитать(Чтение);
// Получаем корневой элемент
Корень = ДокументDOM.ЭлементДокумента;
// Удаляем атрибуты через итерацию по карте атрибутов
АтрибутыДляУдаления = Новый Массив;
Для Каждого Атрибут Из Корень.Атрибуты Цикл
Если СтрНайти(Атрибут.Имя, "xmlns") > 0 Тогда
АтрибутыДляУдаления.Добавить(Атрибут);
КонецЕсли;
КонецЦикла;
Для Каждого Атр Из АтрибутыДляУдаления Цикл
Корень.УдалитьАтрибутУзла(Атр);
КонецЦикла;
Стоит отметить, что DOM в 1С довольно требователен к ресурсам, поэтому для выгрузки реестров на 100 000 позиций лучше вернуться к Способу №2 (Потоковая фильтрация).
Мы рассмотрели различные подходы к решению проблемы лишних пространств имен. Подведем итоги, чтобы выбрать оптимальный вариант:
Помните, что «чистый» XML без пространств имен технически менее совершенен, но в реалиях интеграции с устаревшими или специфичными системами такие методы становятся необходимым инструментом в арсенале программиста 1С.