Как устранить ошибку преобразования данных XDTO «Структура объекта не соответствует типу» при вызове веб-сервиса в 1С?

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

При интеграции 1С со сторонними веб-сервисами (например, сервисами дистрибьюторов) разработчики часто сталкиваются с ситуацией, когда инициализация сервиса проходит успешно, но при вызове конкретной операции платформа выдает исключение:

«Ошибка преобразования данных XDTO: Чтение объекта типа: {...}ArrayOfCatalogResult. Проверка дополнительного свойства: форма: Элемент, имя: item. Структура объекта не соответствует типу».

Проанализируем природу этой ошибки, выясним технические ограничения платформы 1С и разберем практические способы решения задачи исключительно штатными средствами платформы.

Почему возникает ошибка преобразования XDTO?

Разберем по шагам, что происходит в момент вызова метода через WSПрокси:

  1. Несовместимость стилей SOAP (RPC vs Document): В адресе сервиса часто используется сегмент пути, указывающий на формат RPC/Literal (например, /rl/) или специфичную реализацию веб-сервиса (часто на базе PHP). В таких спецификациях возвращаемые массивы упаковываются в контейнеры с элементами item. Платформа 1С при создании прокси-объекта жестко ориентируется на спецификацию Document/Literal (Wrapped). При синтаксическом разборе ответа валидатор XDTO наталкивается на тег item, который не описан в схеме явным образом как допустимое свойство типа, и генерирует исключение.
  2. Конфликт пространств имен и квалификации элементов: В XML-схеме сервиса пространство имен для элементов коллекции может быть объявлено как неквалифицированное, тогда как фактически веб-сервер возвращает элементы с указанием префикса целевого пространства имен (или наоборот). Для механизма XDTO в 1С любое расхождение в квалификации имени узла делает его неизвестным («дополнительным») свойством.

Ограничения использования WSПрокси для тяжелых операций

Вызов методов наподобие получения полного каталога через стандартный WSПрокси несет в себе два фундаментальных ограничения:

  1. Невозможность отключить строгую валидацию: В объекте WSПрокси валидация ответа по внутренней модели XDTO выполняется принудительно. Если структура XML хотя бы на один тег отличается от контракта WSDL, прочитать ответ целиком через прокси невозможно.
  2. Переполнение оперативной памяти: Метод веб-сервиса через WSПрокси считывает весь XML-ответ в оперативную память для построения объектной модели. Если метод возвращает полный каталог номенклатуры объемом в десятки или сотни мегабайт, клиентский или серверный процесс 1С аварийно завершится с ошибкой нехватки памяти.

Способ 1. Переключение точки подключения на Document/Literal

Многие современные внешние API поддерживают параллельную работу по разным схемам. Если сервис предоставляет эндпоинт в формате Document/Literal, достаточно скорректировать адрес подключения в коде.

Посмотрим на пример: если в исходном коде использовался URL с сегментом /rl/ (RPC/Literal), заменим его на /dl/ (Document/Literal):


Определения = Новый WSОпределения("https://api.merlion.com/dl/mlservice3?wsdl", Пользователь, Пароль);
ВебСервис = Новый WSПрокси(Определения, "https://api.merlion.com/dl/mlservice3", "MLService", "MLPort");
ВебСервис.Пользователь = Пользователь;
ВебСервис.Пароль = Пароль;

Результат = ВебСервис.getCatalog("All");

В режиме /dl/ описание WSDL и формируемый XML-ответ строго соответствуют стандартам Document/Literal, поэтому десериализация коллекции происходит без ошибок структуры XDTO.

Способ 2. Локальное исправление WSDL и XSD

Если изменить точку подключения на стороне провайдера невозможно, скорректируем описание сервиса вручную:

  1. Скачаем файл WSDL и связанные с ним XSD-схемы на диск либо сохраним их в макет конфигурации или внешней обработки.
  2. Откроем схему в текстовом редакторе и найдем описание проблемного комплексного типа (в нашем случае ArrayOfCatalogResult).
  3. Внутри объявления типа явно укажем вложенный элемент item с целевым типом элемента каталога и атрибутом maxOccurs="unbounded", а также согласуем директиву elementFormDefault.
  4. Инициализируем WSОпределения, передавая путь к локальному исправленному файлу WSDL, но вызывая реальный удаленный веб-сервер:

ПутьКФайлуWSDL = "C:\WSDL\mlservice3_fixed.wsdl";
Определения = Новый WSОпределения(ПутьКФайлуWSDL, Пользователь, Пароль);
ВебСервис = Новый WSПрокси(Определения, "https://api.merlion.com/rl/mlservice3", "MLService", "MLPort");
ВебСервис.Пользователь = Пользователь;
ВебСервис.Пароль = Пароль;

Результат = ВебСервис.getCatalog("All");

Способ 3. Отправка POST-запроса через HTTPСоединение с потоковым разбором

Рассмотрим наиболее надежный и масштабируемый подход. Для методов, возвращающих большие каталоги, стандартной практикой является отказ от WSПрокси в пользу прямого выполнения HTTP-запроса через объект HTTPСоединение с последующим потоковым чтением XML через ЧтениеXML.

Этот метод полностью устраняет проблемы со строгой проверкой XDTO и не расходует память на хранение всего дерева элементов.

Разберем реализацию по шагам:

  1. Сформируем текст SOAP-конверта с телом запроса.
  2. Инициализируем защищенное соединение и отправим запрос методом POST.
  3. Сохраним ответ во временный файл на диске.
  4. Выполним чтение файла через ЧтениеXML, точечно считывая каждый узел номенклатуры с помощью объекта ФабрикаXDTO или СериализаторXDTO.

Посмотрим на пример кода:


ИмяСервера = "api.merlion.com";
Ресурс = "/rl/mlservice3";

ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение(ИмяСервера, 443, Пользователь, Пароль, , , ЗащищенноеСоединение);

ТекстЗапросаSOAP = 
"<soapenv:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" " +
"xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" " +
"xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" " +
"xmlns:mls=""https://api.merlion.com/rl/mlservice3"">" +
"   <soapenv:Header/>" +
"   <soapenv:Body>" +
"      <mls:getCatalog soapenv:encodingStyle=""http://schemas.xmlsoap.org/soap/encoding/"">" +
"         <cat xsi:type=""xsd:string"">All</cat>" +
"      </mls:getCatalog>" +
"   </soapenv:Body>" +
"</soapenv:Envelope>";

Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
Заголовки.Вставить("SOAPAction", "");

Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);
Запрос.УстановитьТелоИзСтроки(ТекстЗапросаSOAP, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

ВременныйФайл = ПолучитьИмяВременногоФайла("xml");

Попытка
    Ответ = Соединение.ОтправитьДляОбработки(Запрос, ВременныйФайл);
    Если Ответ.КодСостояния = 200 Тогда
        
        // Потоковый разбор полученного XML без переполнения памяти
        Чтение = Новый ЧтениеXML();
        Чтение.ОткрытьФайл(ВременныйФайл);
        
        Пока Чтение.Прочитать() Цикл
            Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента И Чтение.Имя = "item" Тогда
                // Здесь обрабатываем каждый отдельный элемент каталога
                // либо считывая вложенные атрибуты, либо десериализуя отдельный узел
            КонецЕсли;
        КонецЦикла;
        
        Чтение.Закрыть();
    Иначе
        ВызватьИсключение "Ошибка сервиса. Код ответа: " + Ответ.КодСостояния;
    КонецЕсли;
ФиналКода
    УдалитьФайлы(ВременныйФайл);
КонецПопытки;

Такой подход гарантирует стабильность обмена данными в 1С независимо от объема загружаемого каталога и некорректностей в WSDL-описаниях внешнего сервиса.

← На главную