Вызов операций WS-сервисов SAP из 1С (передача в параметрах Объектов XDTO)

Довелось столкнуться в своей работе с вызовом из 1С веб-сервисов SAP. Если с передачей простых типов (в терминах XDTO — значений XDTO) в параметрах операции веб-сервиса, не возникло никаких проблем, то с передачей объекта XDTO пришлось повозиться. Несмотря на то, что в 1С я его заполнял, в SAP он воспринимался как пустой. В интернет особо много полезной информации не нашел, поэтому выкладываю свои наработки по теме.

Полезной оказалась эта ссылка:

http://j1c.ru/j1c/node/45

Но там было предложено решение использовать внешний прокси адаптер. Т.к. проблем с обратным вызовом веб-сервиса (из SAP вызывался веб-сервис 1с) не возникло, то решил, что вполне можно обойтись без внешнего прокси адаптера, только штатными средствами 1С.

Собственно все решение приводить не буду, приведу основные функции, которые в итоге получились.

Не знаю, как тут оформлять код, поэтому пока функции выложу так:

1. Сначала подготавливаем структуру 1С, которая по реквизитам аналогична структуре объекта XDTO: на верхнем уровне она должна содержать ИмяКлюча, соответствующее ИмениПараметра операции WS-сервиса. Значениями структуры могут быть: Значение / Массив / Структура, где в свою очередь Массив может содержать Значения / Структуры

Массив используется для передачи таблиц (Список XDTO)

2. Получаем объект Прокси фабрики XDTO

… описывать не буду, все стандартно (я использовал динамическую ссылку, скорее всего можно и статическую)

3. Вызываем функцию:

// Функция инициализирует параметры метода WS и вызывает метод WS
//
Функция ВыполнитьОперациюWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметров1С = Неопределено) Экспорт

Перем СтруктураПараметровОперацииWS;

СтруктураПараметровОперацииWS = ИнициализироватьСтруктуруПараметровОперацииWS(СтруктураПараметров1С);
ПолучитьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS);
ПреобразоватьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS);

СтрокаПараметровОперацииWS = ПолучитьСтрокуПараметровОперацииWSПоСтруктуре(СтруктураПараметровОперацииWS, "СтруктураПараметровОперацииWS");

Попытка
Если СтруктураПараметровОперацииWS.ВозвращаемоеЗначение = Неопределено Тогда
Выполнить("ПроксиФабрикиXDTO." + ИмяОперацииWS + "(" + СтрокаПараметровОперацииWS + ")");
Иначе
Выполнить("СтруктураПараметровОперацииWS.ВозвращаемоеЗначение = ПроксиФабрикиXDTO." + ИмяОперацииWS + "(" + СтрокаПараметровОперацииWS + ")");
КонецЕсли;
Исключение
КонецПопытки;

СтруктураПараметровОперацииWS.ВозвращаемоеЗначение = ПреобразоватьОбъектXDTOвСтруктуру(СтруктураПараметровОперацииWS.ВозвращаемоеЗначение);
Если ТипЗнч(СтруктураПараметровОперацииWS.Параметры) = Тип("Структура") Тогда
Для Каждого Пар Из СтруктураПараметровОперацииWS.Параметры Цикл
СтруктураПараметровОперацииWS.Параметры.Вставить("Значение", ПреобразоватьОбъектXDTOвСтруктуру(Пар.Значение));
КонецЦикла;
КонецЕсли;

Возврат СтруктураПараметровОперацииWS;

КонецФункции //ВыполнитьОперациюWS


// Процедура заполняет структуру параметров WS по структуре параметров 1С
//
// Параметры:
// ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметров1С, СтруктураПараметровОперацииWS
//
Процедура ПолучитьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS)

Перем ТипОперацииWS;

ПроверятьПараметр1С = (СтруктураПараметровОперацииWS.Параметры1С <> Неопределено);

Для каждого ОперацияWS Из ПроксиФабрикиXDTO.ТочкаПодключения.Интерфейс.Операции Цикл

Если ОперацияWS.Имя = ИмяОперацииWS Тогда

Если ОперацияWS.ВозвращаемоеЗначение <> Неопределено Тогда
СтруктураПараметровОперацииWS.ВозвращаемоеЗначение = ПроксиФабрикиXDTO.ФабрикаXDTO.Создать(ОперацияWS.ВозвращаемоеЗначение.Тип);
КонецЕсли;

// Добавим URIПространстваИмен и Объект типа операции
Для каждого ПакетXDTO ИЗ ПроксиФабрикиXDTO.ФабрикаXDTO.Пакеты Цикл
Для каждого СвойствоXDTO Из ПакетXDTO.КорневыеСвойства Цикл
Если СвойствоXDTO.Имя = ИмяОперацииWS Тогда
СтруктураПараметровОперацииWS.URIПространстваИмен = ПакетXDTO.URIПространстваИмен;
ТипОперацииWS = СвойствоXDTO.Тип;
КонецЕсли;
КонецЦикла;
КонецЦикла;

ОбъектXDTO = ПроксиФабрикиXDTO.ФабрикаXDTO.Создать(ТипОперацииWS);
Для каждого ПараметрОперацииWS Из ОперацияWS.Параметры Цикл

Объект1С = Неопределено;
Если ПроверятьПараметр1С И СтруктураПараметровОперацииWS.Параметры1С.Свойство(ПараметрОперацииWS.Имя, Объект1С) Тогда
ОбъектПараметраОперацииWS = ПолучитьОбъектXDTOпоСтруктуреОбъекта1С(ПроксиФабрикиXDTO, ПараметрОперацииWS.Тип, Объект1С);
ОбъектXDTO[ПараметрОперацииWS.Имя] = ОбъектПараметраОперацииWS;
СтруктураПараметровОперацииWS.Параметры.Вставить(ПараметрОперацииWS.Имя, ОбъектПараметраОперацииWS);
Иначе
СтруктураПараметровОперацииWS.Параметры.Вставить(ПараметрОперацииWS.Имя, Неопределено);
КонецЕсли;

КонецЦикла;
//СтруктураПараметровОперацииWS.ОбъектОперацииWS = ОбъектXDTO;

КонецЕсли;

КонецЦикла;

КонецПроцедуры // ЗаполнитьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS)


// Процедура Преобразует формат параметров WS 1С в формат WS внешней системы (SAP)
//
// Параметры:
// ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS
//
Процедура ПреобразоватьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS)

Для каждого ОперацияWS Из ПроксиФабрикиXDTO.ТочкаПодключения.Интерфейс.Операции Цикл

Если ОперацияWS.Имя = ИмяОперацииWS Тогда

Для каждого Параметр Из СтруктураПараметровОперацииWS.Параметры Цикл

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();

ЗаписьXML.ЗаписатьНачалоЭлемента(ИмяОперацииWS);
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("", СтруктураПараметровОперацииWS.URIПространстваИмен);

ПроксиФабрикиXDTO.ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, Параметр.Значение, Параметр.Ключ);

ЗаписьXML.ЗаписатьКонецЭлемента();

Стр = ЗаписьXML.Закрыть();

// Преобразуем XML (для более сложных случаев надо задействовать XSLT)
Стр = СтрЗаменить(Стр, ИмяОперацииWS, "n0:" + ИмяОперацииWS);
Стр = СтрЗаменить(Стр, "xmlns=", "xmlns:n0=");

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(Стр);
ЧтениеXML.ПерейтиКСодержимому();
ЧтениеXML.Прочитать();

Попытка
СтруктураПараметровОперацииWS.Параметры.Вставить(Параметр.Ключ, ПроксиФабрикиXDTO.ФабрикаXDTO.ПрочитатьXML(ЧтениеXML));
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;

КонецЦикла;

КонецЕсли;

КонецЦикла;

КонецПроцедуры // ПреобразоватьСтруктуруПараметровОперацииWS(ПроксиФабрикиXDTO, ИмяОперацииWS, СтруктураПараметровОперацииWS)


// Функция возвращает строку параметров через запятую из структуры
//
// Параметры:
// Нет
//
Функция ПолучитьСтрокуПараметровОперацииWSПоСтруктуре(СтруктураПараметровОперацииWS, ИмяПеременнойСтруктурыПараметровОперацииWS)

Перем ВозвращаемоеЗнач;

ВозвращаемоеЗнач = "";
Если ТипЗнч(СтруктураПараметровОперацииWS) = Тип("Структура") Тогда
Для каждого Пар Из СтруктураПараметровОперацииWS.Параметры Цикл
ВозвращаемоеЗнач = ВозвращаемоеЗнач + ", " + ИмяПеременнойСтруктурыПараметровОперацииWS + ".Параметры." + Пар.Ключ;
КонецЦикла;
Если ВозвращаемоеЗнач <> "" Тогда
ВозвращаемоеЗнач = Сред(ВозвращаемоеЗнач, 3);
КонецЕсли;
КонецЕсли;

Возврат ВозвращаемоеЗнач;

КонецФункции // ПолучитьСтрокуПараметровОперацииWSПоСтруктуре(СтруктураПараметровОперацииWS, ИмяСтруктурыПараметровОперацииWS)


// Функция возвращает структуру по объекту XDTO
//
Функция ПреобразоватьОбъектXDTOвСтруктуру(ОбъектXDTO)

Перем ВозвращаемоеЗнач;

Если ОбъектXDTO = Неопределено Тогда

ВозвращаемоеЗнач = Неопределено;

ИначеЕсли ТипЗнч(ОбъектXDTO) = Тип("ЗначениеXDTO") Тогда

ВозвращаемоеЗнач = ОбъектXDTO.Значение;

ИначеЕсли ТипЗнч(ОбъектXDTO) = Тип("ОбъектXDTO") Тогда

ВозвращаемоеЗнач = Новый Структура;

Для каждого СвойствоXDTO Из ОбъектXDTO.Свойства() Цикл

ВозвращаемоеЗнач.Вставить(СвойствоXDTO.Имя);

Попытка // свойство - списокXDTO

СписокXDTO = ОбъектXDTO.ПолучитьСписок(СвойствоXDTO);
КоличествоСписка = СписокXDTO.Количество();

МассивЗначений = Новый Массив;
Для Сч = 0 По КоличествоСписка - 1 Цикл
МассивЗначений.Добавить(ПреобразоватьОбъектXDTOвСтруктуру(СписокXDTO.ПолучитьXDTO(Сч)));
КонецЦикла;

ВозвращаемоеЗнач[СвойствоXDTO.Имя] = МассивЗначений;

Исключение // свойство - объектXDTO/значениеXDTO

ВозвращаемоеЗнач[СвойствоXDTO.Имя] = ПреобразоватьОбъектXDTOвСтруктуру(ОбъектXDTO.ПолучитьXDTO(СвойствоXDTO));

КонецПопытки;

КонецЦикла;

КонецЕсли;

Возврат ВозвращаемоеЗнач;

КонецФункции

4. Ключевой является преобразование функция ПреобразоватьСтруктуруПараметровОперацииWS

Я ее реализовал простой СтрЗаменить, т.к. этого оказалось достаточно, но в принципе можно было бы провести XSLT- преобразование (в 1С эта возможность тоже штатная). 

Причина возникновения этой функции в том, что 1С добавляет и в узел, и в его подчиненные элементы префикс имени (не знаю, точно ли передаю термины, т.к. не силен в веб технологиях, надеюсь смысл понятен). А SAP эти префиксы не воспринимает, и потому считает соответствующие значение пустыми. Фактические же префикс надо добавлять только в сам узел (в данном случае узел параметра операции). Это я и делаю с помощью СтрЗаменить.

 

Отвечать и оформлять если что, буду позже, просто думаю, что данный материал может пригодится не только для вызова сервисов SAP, но и для других Web-сервисов.

13 Comments

  1. Kuzja_R

    Спасибо, интересно!

    Для оформления http://infostart.ru/public/66592/

    Reply
  2. adva

    Оформлю позднее.

    Выявил, что ответные параметры из SAP в виде объектов XDTO все же не принимаются (со значениями опять же проблем нет). Входные/выходные параметры, тоже не отрабатывают, в ту сторону уходят, а назад платформа 1с выдает ошибку. Пока для обработной передачи остаются только параметры значения (например в виде строки), что конечно не достаточно, но похоже ограничения самой платформы не обойти. Может ее когда нибудь исправят.

    Пример выдаваемой ошибки:

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

    форма: Элемент

    имя: NameItem: Ошибка преобразования данных XDTO:

    Чтение объекта типа: {urn:sap-com:document:sap:soap:functions:mc-style}NameType — [13,26]

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

    форма: Элемент

    имя: item: Ошибка проверки данных XDTO:

    Reply
  3. Dreadnouth

    не могли бы вы привести более полный код?

    потому что никак не могу понять что же за структуру надо сделать в СтруктураПараметров1С

    и что делает процедура ИнициализироватьСтруктуруПараметровОперацииWS

    заранее благодарен!

    Reply
  4. adva

    (3) Извиняюсь за задержку, здесь не очень часто бываю. Код скорее всего уже не найду, т.к. сменил место работы. СтруктураПараметров1С это:

    1. Сначала подготавливаем структуру 1С, которая по реквизитам аналогична структуре объекта XDTO: на верхнем уровне она должна содержать ИмяКлюча, соответствующее ИмениПараметра операции WS-сервиса. Значениями структуры могут быть: Значение / Массив / Структура, где в свою очередь Массив может содержать Значения / Структуры

    Массив используется для передачи таблиц (Список XDTO)

    Насколько помню:

    ИнициализироватьСтруктуруПараметровОперацииWS по структуре параметров 1С создает по сути не заполненный объект XDTO

    В общем попробую найти код, если найду, выложу

    Reply
  5. BOZKURT

    Спасибо!

    Reply
  6. Trise

    Я знаешь как пробовал делать, указываешь в качестве параметра метода anyType для веб-сервиса и он может у тебя все принимать и все передавать

    Reply
  7. Westbound

    (6) Trise, а если веб-сервис не мой, как я могу изменить данные о параметрах?

    Reply
  8. B2B

    По сути самая важная процедура в данном примере — это «ПреобразоватьСтруктуруПараметровОперацииWS». Она отвязывает значения параметров от пространства имен функции. Совсем не обязательно, как в данном примере, прогонять все параметры через эту процедуру, достаточно обработать только параметры со сложных типов.

    Reply
  9. harmer

    Кто-нибудь сталкивался с проблемой, что имя метода содержит точку. Соответственно при вызове

     Прокси.ИмяМетода.ЧерезТочку(Параметр)

    возникает исключительная ситуация «Поле объекта не обнаружено».

    Reply
  10. adva

    (9) harmer, (опять поздно отвечаю), но разве можно имя метода через точку писать, разве правила имени не такие же, как у любых переменных 1С? Или это не 1с веб-сервис?

    Reply
  11. harmer

    Это не 1С, это SAP.

    Reply
  12. _OVEN_

    Подтверждаю — если 1с-кий Web-сервис в связке с SAP работает на ура! То в SAP-кий Web-сервис — смог передать только элементарные типы данных — строка, число. Структуру — ни в какую. 2-й день не могу АБАПЕРУ передать структуру состоящую из двух реквизитов строчного типа. Автору статьи огромнейший респект.

    Reply
  13. _OVEN_

    Есть другой проверенный подход. В SAP передавать заранее подготовленную строку файла XML (тип string). SAP имеет ограничение длины этой строки — 2 Гб. Для обменов — этого будет достаточно. В 1с создаем пакет XDTO (с таблицами, списками значений и так далее), с помощью которого создадим строку XML. Собственно ее и передадим на Веб-сервис SAP.

    Вот код:

    Функция ВернутьХМЛ()
    
    ЗаписьXML  = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    
    ЗаписьXML.ЗаписатьОбъявлениеXML();
    ПрайсXDTO = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип(«http://www.sample-package.org», «TZ»));
    
    Для Сч = 1 По 2 Цикл
    
    СтрокаПрайсXDTO = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип(«http://www.sample-package.org», «TZ_Line»));
    
    Если Сч = 1 Тогда
    СтрокаПрайсXDTO.Field1_Str = «Автомобиль №1»;
    СтрокаПрайсXDTO.Field_Int  = Число(«12545444»);
    СтрокаПрайсXDTO.Field_Date = ТекущаяДата();
    СтрокаПрайсXDTO.Field_Num  = Число(«4500,87»);
    ИначеЕсли Сч = 2 Тогда
    СтрокаПрайсXDTO.Field1_Str = «Автомобиль №567»;
    СтрокаПрайсXDTO.Field_Int  = Число(«38887410»);
    СтрокаПрайсXDTO.Field_Date = ДобавитьМесяц(ТекущаяДата(),-1);
    СтрокаПрайсXDTO.Field_Num  = Число(«875200,23»);
    КонецЕсли;
    
    
    ПрайсXDTO.TZ_Str.Добавить(СтрокаПрайсXDTO);
    
    КонецЦикла;
    
    ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ПрайсXDTO);
    
    СтрокаXML = ЗаписьXML.Закрыть();
    
    Возврат СтрокаXML;
    
    
    КонецФункции
    
    Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    Урл = УрлВебСервисаСАП;
    УрлПространстваИмен = ПространствоИменВебСервисаСАП;
    
    WSПрокси = Новый WSПрокси(WSОпределение,  УрлПространстваИмен ,  «zws_utn_test_xml», «zws_utn_test_xml_bind» );
    WSПрокси.Пользователь = ЛогинСАП;
    WSПрокси.Пароль       = ПарольСАП;
    
    Фабрика = WSПрокси.ФабрикаXDTO;
    
    ТипWSПараметра_ТЗ = Фабрика.Пакеты.Получить(УрлПространстваИмен).Получить(«Z_UTN_TEST_WS_XML»);
    ТЗ_WS    = Фабрика.Создать(ТипWSПараметра_ТЗ);
    
    Зн_IV_BUDAT =  ВернутьХМЛ();
    
    Результат = WSПрокси.Z_UTN_TEST_WS_XML(Зн_IV_BUDAT);
    
    КонецПроцедуры

    Показать

    Собственно, WS-ссылка и пакета XDTO — прикрепляю в картинках.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *