При работе с внешними веб-сервисами через механизмы 1С:Предприятия разработчики часто сталкиваются с ситуацией, когда сформированный XML-запрос отличается от эталонного (например, полученного в SOAPUI). Одной из наиболее раздражающих проблем является дублирование тегов, особенно когда речь идет о списках или массивах данных. Вместо плоской структуры 1С генерирует вложенный контейнер с тем же именем, что приводит к ошибке обработки запроса на стороне сервера.
В этой статье мы подробно разберем, почему возникает эффект «матрешки», как правильно манипулировать объектами XDTO для списочных типов и какими инструментами можно перехватить исходящий трафик для анализа.
Рассмотрим стандартную ситуацию. В WSDL-описании определен элемент-список, например mobileTariffIdList, внутри которого должны идти элементы mobileTariffId. Разработчик интуитивно пытается создать объект для списка отдельно, заполнить его и присвоить родительскому объекту. Однако именно здесь кроется ловушка.
Проанализируем пример некорректного формирования:
// Ошибка: создание отдельного объекта для списка
ТипОбъектаitemList = Прокси.ФабрикаXDTO.Пакеты.Получить("urn:XXXXXXXX").Получить("MobileTariffIdList");
ОбъектitemList = Прокси.ФабрикаXDTO.Создать(ТипОбъектаitemList);
ОбъектitemList.mobileTariffId.Добавить("235045");
// При присваивании 1С видит, что тип объекта и тип свойства могут конфликтовать,
// и создает дополнительную вложенность для соблюдения структуры XSD.
WSПараметр.mobileTariffIdList = ОбъектitemList;
В результате получается XML, где внутри тега mobileTariffIdList появляется еще один mobileTariffIdList. Кроме того, часто возникает путаница с пространствами имен (xmlns=""), так как 1С пытается изолировать вложенный объект, созданный вручную.
Выясним причину такого поведения: в модели XDTO свойства, имеющие максимальное количество элементов больше единицы (maxOccurs > 1), по умолчанию инициализируются как СписокXDTO. Хотя в отладчике такое свойство может отображаться как Неопределено до момента первой записи, оно уже готово к работе как коллекция.
Разберем правильный подход по шагам. Вместо создания промежуточного объекта, мы должны обращаться непосредственно к свойству созданного родительского контейнера:
Добавить() для вставки значений.Посмотрим на пример правильного кода:
Прокси = Новый WSПрокси(Определение, "urn:XXXXXXXX", "CRMServiceOutService", "HTTP_Port");
// Получаем тип всего запроса
ТипRequest = Прокси.ФабрикаXDTO.Тип("urn:XXXXXXXX", "GetMobileTariffInfoRequest");
WSПараметр = Прокси.ФабрикаXDTO.Создать(ТипRequest);
// ПРАВИЛЬНО: Добавляем элементы напрямую в список родительского объекта
// 1С сама понимает структуру и не создает лишних тегов-контейнеров
WSПараметр.mobileTariffIdList.mobileTariffId.Добавить("235045");
WSПараметр.mobileTariffIdList.mobileTariffId.Добавить("235046");
Результат = Прокси.GetMobileTariffInfo(WSПараметр);
Иногда структура WSDL требует передачи объекта определенного типа прямо в параметры метода. В этом случае полезно создавать объект не через ФабрикаXDTO.Пакеты..., а через метод Тип(). Это гарантирует, что вы используете правильное пространство имен.
Проанализируем ситуацию, когда метод ожидает список как основной параметр:
// Получаем конкретный тип списка из фабрики
ТипСписка = Прокси.ФабрикаXDTO.Тип("urn:XXXXXXXXXXXXXXXX", "MobileTariffIdList");
// Создаем объект этого типа
СписокОбъект = Прокси.ФабрикаXDTO.Создать(ТипСписка);
// Наполняем его данными
СписокОбъект.mobileTariffId.Добавить("235045");
// Передаем объект в метод прокси
Результат = Прокси.GetMobileTariffInfo(СписокОбъект);
Когда внешняя система недоступна, а ошибка сохраняется, крайне важно визуально проанализировать сформированный XML. В 1С есть несколько способов сделать это «на лету» без использования снифферов трафика.
Способ А: Сериализация через ЗаписьXML
Рассмотрим, как выгрузить объект XDTO в строку. Важно использовать ту же фабрику, которая привязана к вашему WSПрокси, иначе возникнет ошибка о несоответствии типов.
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
// Используем фабрику прокси для корректной сериализации
Прокси.ФабрикаXDTO.ЗаписатьXML(Запись, WSПараметр);
ТекстЗапроса = Запись.Закрыть();
Сообщить("Сформированный XML:");
Сообщить(ТекстЗапроса);
Способ Б: Использование СериализатораXDTO
Если вы работаете с объектами, которые можно преобразовать в ОбъектXDTO, можно воспользоваться глобальным сериализатором. Однако для WSПрокси первый способ предпочтительнее, так как он учитывает специфику WSDL.
Если после исправления кода вы все еще видите лишние атрибуты типа xmlns="", следует проанализировать саму XSD-схему. Параметр elementFormDefault="unqualified" в схеме заставляет 1С генерировать вложенные элементы без префикса пространства имен. Если сервер ожидает обратного, это может быть проблемой импорта WSDL. В таких случаях иногда помогает ручная правка файла схемы перед созданием WSОпределения (замена на qualified).
Подведем итог: дублирование тегов в XDTO почти всегда связано с избыточным созданием промежуточных объектов. Помните, что списки внутри XDTO — это живые коллекции, которые создаются автоматически вместе с родительским узлом. Используйте метод Добавить() к свойствам существующего объекта, и структура вашего XML будет в точности соответствовать ожиданиям веб-сервиса.