I have imported a WSDL and use it to send a SOAP request. It looks like this:
<?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...
The problem is the xmlns="urn:xx.WSDL.xxxxxWebService"
part in the Calculate element. The web service cannot accept this. The web service doesn't like namespaces like this...
Using SoapUI I found this request to work just fine:
<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...
So, how do I change the request from the first to the second version? (Without using dirty tricks!)
(Re-importing is not a problem if this would result in the proper request format.)
Again: no dirty tricks allowed, like hacking the request stream to modify it!
And while I haven't completely tested it, it seems that C#/VS2010 and Delphi 2010 are also unable to use the web service that I'm trying to call. A web service that seems to be written in Java. SoapUI happens to be written in Java, thus we have a Java client talking to a Java service, which seems to work just fine. But any other client?
Anyways, time to add two more tags: "Java", since it's a Java service, and "vs2010" because .NET also dislikes this service.
And I was about to write a wrapper around this service in .NET, hoping that would work... It doesn't. So this is a very serious flaw, possibly a Java flaw...
Add attribute exclude-result-prefixes in <xsl:stylesheet> . This ensures the namespaces are not added in the output.
Once a namespace prefix is created, it cannot be changed or deleted. The workaround is to move all your code to a new Developer Organization, where you can setup the desired Namespace Prefix.
An XML namespace is a means of qualifying element and attribute names to disambiguate them from other names in the same document. This section provides a brief description of XML namespaces and how they are used in SOAP. For complete information, see http://www.w3.org/TR/REC-xml-names/
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With