I have a very interesting issue when I call a SOAP method with my client, I must pass a parameter which is of type Array_Of_Int(Array_Of_Int = array of Integer), the problem is that when the array is being generated in the request, it generates the following
<ArrayParam>
<item>12345</item>
<item>23456</item>
<item>34567</item>
</ArrayParam>
but I believe the server expects
<ArrayParam>12345</ArrayParam>
<ArrayParam>23456</ArrayParam>
<ArrayParam>34567</ArrayParam>
I'm pretty sure that Delphi has a workaround for this issue somehow in the RegisterSerializeOptions or RegisterInvokeOptions however I can't seem to find the issue, thoughts?!
Thank you all for your time, I'm using Delphi 2010.
EDIT: in order to fix this issue, as Bruneau mentioned, we need to have the following code added in the initialization section of generated .pas file:
InvRegistry.RegisterInvokeOptions(TypeInfo(<ServerInterfaceNameHere>), ioDocument);
However that imposes another issue, the namespace, as a quick and pretty elegant fix, I've added the following code in the THTTPRio's OnBeforeExecute method
procedure TMyDataModule.MyRioBeforeExecute(const MethodName: string; SOAPRequest: TStream);
procedure FixNamespaces;
var
LStrings: TStringList;
begin
LStrings := TStringList.Create;
try
SOAPRequest.Position := 0;
LStrings.LoadFromStream(SOAPRequest);
SOAPRequest.Position := 0;
SOAPRequest.Size := 0;
LStrings.Text := StringReplace(LStrings.Text, MethodName, 'NS1:' + MethodName, [rfReplaceAll]);
LStrings.Text := StringReplace(LStrings.Text, MethodName + ' xmlns', MethodName + ' xmlns:NS1', []);
LStrings.Sav开发者_运维知识库eToStream(SOAPRequest);
SOAPRequest.Position := 0;
finally
FreeAndNil(LStrings);
end; // tryf
end; // procedure FixNamespaces;
begin
FixNamespaces;
end;
The above is just a fix, I really hope I can find a much cleaner and elegant solution to this issue, if anyone knows, please DO post your answer.
The two serialization options you described are both valid and needed. The issue is that from a language/native point of view, Delphi represents both of them with a dynamic array (Array_Of_Int = array of Integer). So, the runtime must be told whether to serialize for a "Pure Collection" (the first kind with elements and the outer ArrayParam) or "unbounded elements" ("ArrayParam" elements).
In earlier versions, including 2010, you could instruct the runtime to serialize as unbounded elements with the following registration:
RemClassRegistry.RegisterSerializeOptions(TypeInfo(Array_Of_Int), [xoInlineArrays]);
If the type is used in a property, you could also simply tag the property itself as unbounded, as in:
property propName: Array_Of_Int Index (IS_UNBD) read FName write FName;
The drawback of the registration approach is that it does not allow one to (re)use the type for both serializations. In Delphi XE this was remedied and now the type is never registered for a particular scheme. Instead each Dynamic Array property or parameter specifies whether it's a "Pure Collection" vs. "Unbounded Element", eliminating the need to have distinct Dynamic Array of Integers for each serialization.
Cheers,
Bruneau
Since no one cares to post their answer or have no other idea on how to fix this issue, I'll just post my fix until others can come out with a more elegant solution than editing the request.
Make sure the next line of code is added in the initialization section of the *.pas file generated when you imported the WSDL file(big thanks to Bruneau for pointing this out)
InvRegistry.RegisterInvokeOptions(TypeInfo(<ServerInterfaceNameHere>), ioDocument);
However that imposes another issue, the namespace, as a quick and pretty elegant fix, I've added the following code in the THTTPRio's OnBeforeExecute method
procedure TMyDataModule.MyRioBeforeExecute(const MethodName: string; SOAPRequest: TStream);
procedure FixNamespaces;
var
LStrings: TStringList;
begin
LStrings := TStringList.Create;
try
SOAPRequest.Position := 0;
LStrings.LoadFromStream(SOAPRequest);
SOAPRequest.Position := 0;
SOAPRequest.Size := 0;
LStrings.Text := StringReplace(LStrings.Text, MethodName, 'NS1:' + MethodName, [rfReplaceAll]);
LStrings.Text := StringReplace(LStrings.Text, MethodName + ' xmlns', MethodName + ' xmlns:NS1', []);
LStrings.SaveToStream(SOAPRequest);
SOAPRequest.Position := 0;
finally
FreeAndNil(LStrings);
end; // tryf
end; // procedure FixNamespaces;
begin
FixNamespaces;
// other possible issue to be fixed -- if any
end;
精彩评论