При интеграции 1С с внешними веб-сервисами (например, сервисами поставщика TVH) через протокол SOAP программисты часто сталкиваются с ошибкой «Неверный формат». Эта проблема обычно возникает не из-за ошибок в логике кода, а из-за неправильного формирования структуры XML-пакета, который ожидает сервер. Особенно это актуально, когда в WSDL-описании используются вложенные типы и коллекции (массивы). Рассмотрим подробно, как правильно описывать такие структуры с помощью фабрики XDTO и избегать типичных ошибок.
Разберем ситуацию: у нас есть метод inquiry, который должен принимать данные о запросе цены. В инструменте SOAP UI запрос выглядит и работает корректно, но в 1С попытка передать созданные объекты Inquiry_WSInqHeadRow напрямую в функцию приводит к ошибке. Проанализируем причину. В SOAP-протоколе структура данных часто представляет собой не просто плоский набор полей, а иерархию. Объект «Строка заголовка» (Row) почти всегда должен быть вложен в объект-контейнер «Заголовок».
Если мы передаем в метод объект типа Inquiry_WSInqHeadRow, а сервер ожидает Inquiry_WSInqHead, внутри которого находится массив или одиночный элемент нужной нам строки, 1С сформирует XML с именами тегов, которые сервер не сможет распознать (десериализовать). Выясним, как построить эту цепочку правильно.
Прежде всего, нам необходимо создать объект WSПрокси и получить доступ к фабрике XDTO, которая содержит определения всех типов данных сервиса. Рассмотрим пример инициализации:
Прокси = WSСсылки.TVH.СоздатьWSПрокси("http://service.webservices.tvh.com", "TvhwsService", "TvhwsServiceHttpSoap12Endpoint");
// Получаем SessionID через метод login
ID = Прокси.login("Логин", "Пароль");
Важно помнить, что для создания объектов нам нужно знать точные пространства имен (Namespace URI). В сервисах TVH часто используются разные URI для самой функции и для структур данных. Проанализируем пример получения типов:
URI_Структуры = "http://struct.webservices.tvh.com/xsd";
ТипКонтейнера = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqHead");
ТипСтроки = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqHeadRow");
Теперь рассмотрим ключевой момент. Для успешного вызова операции нам нужно создать «матрешку» из объектов. Сначала создается объект данных (строка), затем контейнер, в который эта строка помещается. Посмотрим на примере заголовка запроса:
// 1. Создаем саму строку с данными клиента
wsInqHeadRow = Прокси.ФабрикаXDTO.Создать(ТипСтроки);
wsInqHeadRow.customerCode = "12345";
wsInqHeadRow.customerContactName = "Иван Иванов";
// 2. Создаем контейнер-заголовок
wsInqHead = Прокси.ФабрикаXDTO.Создать(ТипКонтейнера);
// 3. Помещаем строку в контейнер
// Если в схеме это одиночный элемент:
wsInqHead.wsInqHeadRow = wsInqHeadRow;
Здесь важно обратить внимание на свойство wsInqHead.wsInqHeadRow. Его имя должно точно соответствовать имени свойства в типе Inquiry_WSInqHead, описанном в WSDL.
В большинстве SOAP-сервисов строки товаров или запросов передаются в виде списков (массивов). В схеме XSD это обозначается атрибутом maxOccurs="unbounded". Если свойство является списком, простая вставка объекта через оператор присваивания может не сработать или привести к ошибке. Разберем правильный подход с использованием метода Добавить() или ПолучитьСписок():
// Создаем тип для контейнера строк линий
ТипКонтейнераЛиний = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqLine");
ТипСтрокиЛинии = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqLineRow");
wsInqLine = Прокси.ФабрикаXDTO.Создать(ТипКонтейнераЛиний);
wsInqLineRow = Прокси.ФабрикаXDTO.Создать(ТипСтрокиЛинии);
wsInqLineRow.partNumber = "PART001";
wsInqLineRow.lineNumber = 1;
// Если свойство wsInqLineRow является списком (Array)
wsInqLine.wsInqLineRow.Добавить(wsInqLineRow);
Использование метода Добавить() гарантирует, что 1С правильно сформирует последовательность тегов в результирующем XML. Если список должен быть пустым, убедитесь, нужно ли вообще создавать контейнер, так как некоторые Java-серверы болезненно реагируют на пустые теги.
Проанализируем ситуацию, когда даже при правильной вложенности сервер выдает ошибку. Часто это связано с Namespace (пространствами имен). При создании типа через Прокси.ФабрикаXDTO.Тип() крайне важно соблюдать точность URI. Даже отсутствие слеша в конце строки или разница между http и https сделает тип «чужим» для фабрики.
Также стоит проверить настройку «Форма XML» в пакете XDTO. Существует два варианта: Qualified (квалифицированный) и Unqualified (неквалифицированный). Если сервер ожидает, что каждый вложенный тег будет иметь префикс пространства имен (например, ns1:customerCode), а 1С отправляет его без префикса, возникнет ошибка формата. В WS-ссылке это настраивается в свойствах пакета.
Сервисы, подобные TVH, чувствительны к типам данных. Рассмотрим пример: поле quantity (количество) имеет тип double. Если вы передадите туда пустую строку "", сервер вернет ошибку. Если поле обязательно, но данных нет, нужно проверить свойство Nillable в схеме XSD.
Если свойство может принимать значение NULL, в 1С это реализуется следующим образом:
// Устанавливаем значение как Неопределено, чтобы в XML появился атрибут xsi:nil="true"
wsInqLineRow.Установить("customerPartNumber", Неопределено);
SOAP-серверы на базе Java (Axis, JAX-WS) крайне требовательны к последовательности тегов. В отличие от обычного XML, где порядок свойств в объекте может быть произвольным, в SOAP-сообщении элементы должны идти строго в том порядке, в котором они описаны в sequence внутри WSDL. 1С при создании объекта через фабрику XDTO обычно соблюдает этот порядок автоматически, но если вы динамически расширяете типы или используете нетипизированные объекты, за этим нужно следить вручную.
Объединим все вышесказанное в единый пример правильного вызова сервиса:
// Инициализация
Прокси = WSСсылки.TVH.СоздатьWSПрокси("http://service.webservices.tvh.com", "TvhwsService", "TvhwsServiceHttpSoap12Endpoint");
URI_Структуры = "http://struct.webservices.tvh.com/xsd";
// Подготовка заголовка (Head)
ТипHead = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqHead");
ТипHeadRow = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqHeadRow");
wsHead = Прокси.ФабрикаXDTO.Создать(ТипHead);
wsHeadRow = Прокси.ФабрикаXDTO.Создать(ТипHeadRow);
wsHeadRow.customerCode = "99999";
wsHead.wsInqHeadRow = wsHeadRow; // Вкладываем строку в контейнер
// Подготовка линий (Lines)
ТипLine = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqLine");
ТипLineRow = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqLineRow");
wsLine = Прокси.ФабрикаXDTO.Создать(ТипLine);
wsLineRow = Прокси.ФабрикаXDTO.Создать(ТипLineRow);
wsLineRow.partNumber = "123456";
wsLine.wsInqLineRow.Добавить(wsLineRow); // Добавляем в список
// Подготовка текста (Text)
ТипText = Прокси.ФабрикаXDTO.Тип(URI_Структуры, "Inquiry_WSInqText");
wsText = Прокси.ФабрикаXDTO.Создать(ТипText);
// Вызов функции с передачей объектов-контейнеров
Результат = Прокси.inquiry(ID.sessionId, wsHead, wsLine, wsText);
Если решение все еще не найдено, воспользуйтесь следующими методами:
ns:) и иерархии вложенности.login возвращает временный токен, который нужно обновлять при длительной работе.Прокси.ФабрикаXDTO.Создать(ТипXDTO), а не через структуру.Таким образом, успех интеграции с SOAP в 1С зависит от точности воссоздания модели данных, описанной в WSDL. Использование объектов-контейнеров и методов работы со списками XDTO позволяет формировать корректные запросы, которые будут приняты внешним сервисом без ошибок формата.