Почему XDTO дублирует теги при формировании SOAP-запроса и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с внешними веб-сервисами через механизмы 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С пытается изолировать вложенный объект, созданный вручную.

Решение №1: Использование метода Добавить() напрямую

Выясним причину такого поведения: в модели XDTO свойства, имеющие максимальное количество элементов больше единицы (maxOccurs > 1), по умолчанию инициализируются как СписокXDTO. Хотя в отладчике такое свойство может отображаться как Неопределено до момента первой записи, оно уже готово к работе как коллекция.

Разберем правильный подход по шагам. Вместо создания промежуточного объекта, мы должны обращаться непосредственно к свойству созданного родительского контейнера:

  1. Создаем основной объект запроса через фабрику.
  2. Обращаемся к свойству-списку родителя.
  3. Используем метод Добавить() для вставки значений.

Посмотрим на пример правильного кода:


Прокси = Новый 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Параметр);

Решение №2: Работа с типами в стиле Document/Literal

Иногда структура WSDL требует передачи объекта определенного типа прямо в параметры метода. В этом случае полезно создавать объект не через ФабрикаXDTO.Пакеты..., а через метод Тип(). Это гарантирует, что вы используете правильное пространство имен.

Проанализируем ситуацию, когда метод ожидает список как основной параметр:


// Получаем конкретный тип списка из фабрики
ТипСписка = Прокси.ФабрикаXDTO.Тип("urn:XXXXXXXXXXXXXXXX", "MobileTariffIdList");
// Создаем объект этого типа
СписокОбъект = Прокси.ФабрикаXDTO.Создать(ТипСписка);

// Наполняем его данными
СписокОбъект.mobileTariffId.Добавить("235045");

// Передаем объект в метод прокси
Результат = Прокси.GetMobileTariffInfo(СписокОбъект);

Как отладить и увидеть реальный текст XML-запроса?

Когда внешняя система недоступна, а ошибка сохраняется, крайне важно визуально проанализировать сформированный XML. В 1С есть несколько способов сделать это «на лету» без использования снифферов трафика.

Способ А: Сериализация через ЗаписьXML

Рассмотрим, как выгрузить объект XDTO в строку. Важно использовать ту же фабрику, которая привязана к вашему WSПрокси, иначе возникнет ошибка о несоответствии типов.


Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();

// Используем фабрику прокси для корректной сериализации
Прокси.ФабрикаXDTO.ЗаписатьXML(Запись, WSПараметр);

ТекстЗапроса = Запись.Закрыть();
Сообщить("Сформированный XML:");
Сообщить(ТекстЗапроса);

Способ Б: Использование СериализатораXDTO

Если вы работаете с объектами, которые можно преобразовать в ОбъектXDTO, можно воспользоваться глобальным сериализатором. Однако для WSПрокси первый способ предпочтительнее, так как он учитывает специфику WSDL.

Особенности настройки elementFormDefault

Если после исправления кода вы все еще видите лишние атрибуты типа xmlns="", следует проанализировать саму XSD-схему. Параметр elementFormDefault="unqualified" в схеме заставляет 1С генерировать вложенные элементы без префикса пространства имен. Если сервер ожидает обратного, это может быть проблемой импорта WSDL. В таких случаях иногда помогает ручная правка файла схемы перед созданием WSОпределения (замена на qualified).

Заключение

Подведем итог: дублирование тегов в XDTO почти всегда связано с избыточным созданием промежуточных объектов. Помните, что списки внутри XDTO — это живые коллекции, которые создаются автоматически вместе с родительским узлом. Используйте метод Добавить() к свойствам существующего объекта, и структура вашего XML будет в точности соответствовать ожиданиям веб-сервиса.

← На главную