При интеграции 1С с внешними системами (особенно в авиационной сфере, логистике или при работе с банковскими API) программисты часто сталкиваются с необходимостью формирования сложных SOAP-сообщений. Одной из специфических задач является вставка целого XML-документа внутрь другого XML-файла в виде секции CDATA. В этой статье мы разберем, как правильно реализовать такой механизм, используя стандартные средства платформы 1С:Предприятие 8.3.
Рассмотрим ситуацию на примере формирования сообщения XFFM (Flight Manifest) стандарта IATA Cargo-XML. Внешний сервис ожидает стандартную SOAP-оболочку (Envelope), внутри которой в одном из элементов (например, api2:xffm) должен находиться основной блок данных, экранированный секцией CDATA. Это делается для того, чтобы парсер основной XML-структуры не пытался интерпретировать вложенные теги как часть основного документа.
Проанализируем типичную структуру такого сообщения:
soapenv:Envelope, soapenv:Header и soapenv:Body.api2:sendMessageXFFM.Самый надежный способ сформировать такой файл — это разделить процесс на два этапа. Сначала мы создаем внутренний XML-документ, преобразуем его в строку, а затем записываем эту строку в основной XML-файл через специальный метод ЗаписатьСекциюCDATA(). Разберем этот процесс по шагам.
Шаг 1: Формируем внутреннее сообщение в строку
Для этого создадим временный объект ЗаписьXML, который будет писать данные в память (в строку), а не сразу в файл.
// Создаем объект для записи внутреннего XML
ЗаписьВнутренняя = Новый ЗаписьXML;
ЗаписьВнутренняя.УстановитьСтроку("UTF-8");
// Записываем структуру внутреннего сообщения (например, манифест рейса)
ЗаписьВнутренняя.ЗаписатьНачалоЭлемента("ns0:FlightManifest", "http://www.iata.org/IATA/2005/00/CargoXML");
ЗаписьВнутренняя.ЗаписатьНачалоЭлемента("MessageHeader");
ЗаписьВнутренняя.ЗаписатьТекст("PMD_240822162004");
ЗаписьВнутренняя.ЗаписатьКонецЭлемента(); // MessageHeader
// ... добавление остальных узлов манифеста ...
ЗаписьВнутренняя.ЗаписатьКонецЭлемента(); // FlightManifest
// Получаем сформированный XML в виде строки
ТекстВнутреннегоXML = ЗаписьВнутренняя.Закрыть();
Шаг 2: Формируем основной SOAP-конверт
Теперь создаем основной объект ЗаписьXML и вставляем туда полученную ранее строку.
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку("UTF-8");
Запись.ЗаписатьОбъявлениеXML();
// Формируем SOAP-оболочку
Запись.ЗаписатьНачалоЭлемента("soapenv:Envelope");
Запись.ЗаписатьАтрибут("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
Запись.ЗаписатьАтрибут("xmlns:api2", "http://api.example.com/v2");
Запись.ЗаписатьНачалоЭлемента("soapenv:Body");
Запись.ЗаписатьНачалоЭлемента("api2:sendMessageXFFM");
// Записываем статические параметры
Запись.ЗаписатьНачалоЭлемента("api2:airline");
Запись.ЗаписатьТекст("PMD");
Запись.ЗаписатьКонецЭлемента();
// ВАЖНЫЙ МОМЕНТ: Записываем вложенный XML как CDATA
Запись.ЗаписатьНачалоЭлемента("api2:xffm");
Запись.ЗаписатьСекциюCDATA(ТекстВнутреннегоXML);
Запись.ЗаписатьКонецЭлемента(); // api2:xffm
Запись.ЗаписатьКонецЭлемента(); // api2:sendMessageXFFM
Запись.ЗаписатьКонецЭлемента(); // soapenv:Body
Запись.ЗаписатьКонецЭлемента(); // soapenv:Envelope
Результат = Запись.Закрыть();
Проанализируем технические нюансы, которые могут возникнуть при использовании метода ЗаписатьСекциюCDATA():
]]>, это приведет к преждевременному закрытию секции CDATA и ошибке парсинга. Рекомендуется программно проверять и экранировать такие сочетания символов перед записью.УстановитьСтроку(), совпадает для обоих объектов записи. Обычно используется "UTF-8".Закрыть(), 1С не добавляет строку <?xml version="1.0" ?>, если вы явно не вызвали ЗаписатьОбъявлениеXML(). В большинстве случаев внутри CDATA заголовок не нужен, достаточно только корневого элемента.Если у вас есть XSD-схема для вложенного сообщения (например, схема манифеста IATA), использование XDTO-пакетов будет более профессиональным решением. Проанализируем преимущества этого метода:
ns0:, система расставит их автоматически согласно URI из схемы.ЗаписатьСекциюCDATA().Пример получения строки из XDTO для вставки в CDATA:
// Предположим, у нас есть заполненный объект XDTO "МанифестОбъект"
ЗаписьXDTO = Новый ЗаписьXML;
ЗаписьXDTO.УстановитьСтроку();
ФабрикаXDTO.Записать(ЗаписьXDTO, МанифестОбъект);
СтрокаДляCDATA = ЗаписьXDTO.Закрыть();
// Далее используем СтрокаДляCDATA в основном коде
Хотя в 1С есть механизм WS-ссылки для автоматической работы с SOAP, в ситуациях с CDATA он часто оказывается бесполезен. Выясним причину: большинство веб-сервисов, использующих такой формат, объявляют параметр входа как обычную строку (string). Если вы импортируете WSDL такого сервиса, 1С предложит вам передать просто текст. Чтобы этот текст был валидным XML-документом, его всё равно придется формировать вручную одним из способов, описанных выше.
Кроме того, ручное формирование XML (через ЗаписьXML) дает полный контроль над префиксами и атрибутами в SOAP-конверте, что критично для систем с очень строгой валидацией, которые не принимают стандартные пакеты, генерируемые 1С автоматически.
При отладке формирования сложных XML-структур мы рекомендуем использовать форматированный вывод. Для этого при инициализации ЗаписьXML можно использовать параметры:
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Истина, Истина, Символы.Таб);
Запись.УстановитьСтроку(ПараметрыЗаписи);
Параметр Отступ = Истина позволит вам прочитать сформированный файл глазами и увидеть структуру вложенности. Однако помните, что для финальной отправки в промышленную систему отступы лучше отключать, так как некоторые системы безопасности могут интерпретировать лишние пробелы как изменение контрольной суммы сообщения.
Таким образом, комбинируя ручную запись через ЗаписьXML и использование CDATA, можно реализовать интеграцию с самыми сложными зарубежными и ведомственными системами, соблюдая все требования протоколов передачи данных.