Removing the namespace from SOAP request

两盒软妹~` 提交于 2019-11-30 08:59:49

If a Service expects:

  <col:Calculate>
     <ContractdocumentIn>
         <AL>

and Delphi SOAP is sending...

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

... the problem is that ContractdocumentIn is an unqualified element and (until Delphi XE) Delphi SOAP did not support unqualified elements that are top level elements of an operation. Top level elements are parameters of the function and there is nowhere to store the fact that the underlying element must be unqualified; for elements that map to properties, we use the Index of the property to store away the IS_UNQL flag.

BTW, it's not necessary to use a prefix. The Service will (should) also accept:

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

The latter is more verbose but it's equivalent to the prefix case.

In Delphi XE the importer stores away the fact that a particular parameter maps to an unqualified element and the runtime acts on this information. I've posted patches based on the XE implementation for D2010 and D2007 in the newsgroup when it came up in a thread recently:

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

If someone needs access to them (they were in the attachments area but might have scrolled off), please email me and I'll make them available. [bbabet at embarcadero dot com]

Cheers,

Bruneau

OMG! It took lots of coffee and plenty of sleep depravation but I managed to solve my problem! It's reasonable simple too...
First I import the WSDL, as expected. This will generate several TRemotable classes. Then, for each TRemotable which needs a different namespace, I override the ObjectToSOAP() method! (And include XMLIntf to the WSDL source.) In my case with code like this for several of the remotable types:

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;

Which worked in Delphi XE. In Delphi 2007 I had to use units XMLIntf and XMLDoc plus this code on the input type:

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;

It is a hack, in my opinion. But it's not a very dirty one. It's a bit of experimenting, capturing the SOAP requests and responses to check their content and to see if it uses the proper namespaces. Unfortunately, Delphi XE does a far better job at this than Delphi 2007.

Still, I keep this Q open for any better solutions...


Btw, to add the col: to the output, I also had to change this line in the WSDL RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'Calculate'); to this: RemClassRegistry.RegisterXSClass(Calculate, 'http://colan.ogconnect.service.wzp/', 'cal:Calculate');. The result then becomes <cal:Calculate xmlns:cal="http://example.webservice/">. Ont more thing needs to be done, though: moving that xmlns:cal to the xml header. But as it is now, it works for me.


Another note: for the WSDL I used the following settings: 'One Outparam is return', 'Unwind literal params', 'Generate destructors', 'Warning comments', 'emit literal types', 'Map string to widestring'. Other options are: 'Generate verbose information about types and interfaces', 'Ignore porttypes with HTTP Bindings', 'Validate enumeration members', 'Import fault types', 'Import header types', 'Process included and imported schemas', 'Generate class alias as class types', 'Allow Out parameters' and 'Process nillable and optional elements'. The emit literal types was practical because it generates a class around the single method that I was calling. Unfortunately, this won't help much either, although the class would help you to modify the SOAP request on the upper level within the envelope by overriding the ObjectToSOAP() method.
Creation of the envelope itself is in the SOAPEnv unit and it's used in the OPToSOAPDomConv unit. Unfortunately, I haven't found an easy method to access the envelope itself to alter the header to add this additional namespace. Then again, I could override the TSOAPDomConv class with my own version that does add the additional namespace. But the code is working for me now, and as my father told me, when he learned me to program: never fix anything that isn't broken.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!