Удаление пространства имен из запроса SOAP

Я импортировал WSDL и использовал его для отправки запроса SOAP. Это выглядит так:

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Body>
        <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
            <ContractdocumentIn>
                <AL>
                ...More XML...

Проблема заключается в части xmlns="urn:xx.WSDL.xxxxxWebService" в элементе Calculate. Веб-служба не может этого принять. Веб-служба не любит пространства имен, подобные этому...
Используя SoapUI Я нашел этот запрос очень хорошим:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:col="http://example.com.service.xxx/">
    <SOAP-ENV:Body>
        <col:Calculate>
            <ContractdocumentIn>
                <AL>
                    ...More XML...

Итак, как мне изменить запрос от первой ко второй версии? (Без использования грязных трюков!)
(Повторное импортирование не является проблемой, если это приведет к правильному формату запроса.)



Снова: никаких грязных трюков не было, например, взломать поток запросов, чтобы изменить его!


И пока я не полностью протестировал его, кажется, что С#/VS2010 и Delphi 2010 также не могут использовать веб-службу, которую я пытаюсь вызвать. Веб-сервис, который, кажется, написан на Java. SoapUI, написанный на Java, поэтому у нас есть клиент Java, который разговаривает с Java-службой, которая, кажется, работает нормально. Но любой другой клиент?
В любом случае, время добавить еще два тега: "Java", так как это Java-сервис и "vs2010", потому что .NET также не любит эту службу.
И я собирался написать обертку вокруг этой службы в .NET, надеясь, что это сработает... Это не так. Так что это очень серьезный недостаток, возможно, недостаток Java...

Ответ 1

Если Служба ожидает:

  <col:Calculate>
     <ContractdocumentIn>
         <AL>

и Delphi SOAP отправляет...

    <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
        <ContractdocumentIn>
            <AL>

... проблема заключается в том, что ContractdocumentIn является неквалифицированным элементом и (до Delphi XE). Delphi SOAP не поддерживает неквалифицированные элементы, которые являются элементами верхнего уровня операции. Элементы верхнего уровня являются параметрами функции, и нигде не следует хранить факт, что базовый элемент должен быть неквалифицирован; для элементов, которые отображаются в свойствах, мы используем индекс свойства, чтобы сохранить флаг IS_UNQL.

Кстати, нет необходимости использовать префикс. Служба будет (должна) также принять:

    <Calculate xmlns="urn:xx.WSDL.xxxxxWebService">
        <ContractdocumentIn xmlns="">
            <AL>

Последнее более подробное, но оно эквивалентно случаю префикса.

В Delphi XE импортер сохраняет в стороне тот факт, что конкретный параметр сопоставляется с неквалифицированным элементом, и среда выполнения воздействует на эту информацию. Я опубликовал исправления на основе реализации XE для D2010 и D2007 в группе новостей, когда он недавно появился в потоке:

https://forums.embarcadero.com/thread.jspa?threadID=43057

Если кому-то нужен доступ к ним (они были в области вложений, но могли прокрутиться), напишите мне, и я сделаю их доступными. [bbabet at embarcadero dot com]

Приветствия,

Брюно

Ответ 2

OMG! Было много кофе и много разврата сна, но мне удалось решить мою проблему! Это тоже разумно...
Сначала я импортирую WSDL, как и ожидалось. Это создаст несколько классов TRemotable. Затем для каждого TRemotable, которому требуется другое пространство имен, я переопределяю метод ObjectToSOAP()! (И включите XMLIntf в источник WSDL.) В моем случае с кодом, подобным этому для нескольких удаленных типов:

function AL2.ObjectToSOAP( RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const NodeName, NodeNamespace, ChildNamespace: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString ): IXMLNode;
begin
  Result := inherited ObjectToSOAP( RootNode, ParentNode, ObjConverter, NodeName, '', '', ObjConvOpts, RefID );
end;

Что работало в Delphi XE. В Delphi 2007 мне пришлось использовать единицы XMLIntf и XMLDoc плюс этот код для типа ввода:

function ContractdocumentInType.ObjectToSOAP(RootNode, ParentNode: IXMLNode; const ObjConverter: IObjConverter; const Name, URI: InvString; ObjConvOpts: TObjectConvertOptions; out RefID: InvString): IXMLNode;

  procedure AlterChildren(Child: IXMLNode);
  var
    I: Integer;
  begin
    if (Child.NodeType = ntElement) then Child.SetAttributeNS('xmlns', '', '');
    for I := 0 to Pred(Child.ChildNodes.Count) do
      AlterChildren(Child.ChildNodes[I]);
  end;

begin
  Result := inherited ObjectToSOAP(RootNode, ParentNode, ObjConverter, Name, '', ObjConvOpts, RefID);
  AlterChildren(Result);
end;

Это, по-моему, взлом. Но это не очень грязный. Это немного экспериментирует, захватывая SOAP-запросы и ответы, чтобы проверить их содержимое и посмотреть, использует ли он правильные пространства имен. К сожалению, Delphi XE работает намного лучше, чем Delphi 2007.

Тем не менее, я держу этот Q открытым для любых лучших решений...


Btw, чтобы добавить col: к выводу, мне также пришлось изменить эту строку в WSDL RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'Calculate'); на это: RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'cal:Calculate');. Тогда результат будет <cal:Calculate xmlns:cal="http://example.webservice/">. Однако нужно еще что-то сделать: перемещение этого xmlns:cal в заголовок xml. Но как сейчас, это работает для меня.


Еще одно примечание: для WSDL я использовал следующие настройки: "One Outparam is return", "Unwind literal params", "Generate destructors", "Warning comments", " emit literal types, 'Сформировать строку в ширину'. Другие варианты:" Генерировать подробные сведения о типах и интерфейсах "," Игнорировать типы портов с HTTP-привязками "," Проверять элементы перечисления "," Импортировать типы ошибок "," Импортировать типы заголовков "," Включенные процессы и импортированные схемы "," Создать class alias как типы классов ',' Allow Out parameters 'и' Process nillable and optional elements '. emit literal types был практичным, потому что он генерирует класс вокруг одного метода, который я вызывал. К сожалению, это тоже не поможет, хотя класс поможет вам изменить запрос SOAP на верхнем уровне в конверте, переопределив метод ObjectToSOAP().
Создание самого конверта находится в блоке SOAPEnv и он используется в блоке OPToSOAPDomConv. К сожалению, я не нашел простой способ доступа к самому конверту, чтобы изменить заголовок, чтобы добавить это дополнительное пространство имен. Опять же, я могу переопределить класс TSOAPDomConv своей собственной версией, которая добавит дополнительное пространство имен. Но теперь код работает для меня, и, как сказал мой отец, когда он научил меня программировать: никогда не исправлять ничего, что не сломалось.