Вызов операций 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 не будет опубликован. Обязательные поля помечены *