Unsure. You could use the units fphttpclient and DOM to transfer your own XML. I guess, the complexity comes with the automated binding.
Ok, here comes another "opinion". I hate automated remoting. To me it feels a little brittle and over cooked. I haven't dont much networking stuff in years, but here is how I did it in C#.
Write a HTTP server. Use the HTTP querystring for parameters. (I reparse the querystring, because I dont need the name/values). The sample code remotes 7 functions. Its pretty easy to return arrays.
program server;
{$mode objfpc}{$H+}
uses
FileUtil,
fphttpserver,
HttpDefs,
Math,
SysUtils,
UriParser,
Variants;
type
TMyRemotingServer = class(TFPHttpServer)
procedure HandleRequest(var ARequest: TFPHTTPConnectionRequest; var AResponse: TFPHTTPConnectionResponse);
override;
end;
procedure TMyRemotingServer.HandleRequest(var ARequest: TFPHTTPConnectionRequest;
var AResponse: TFPHTTPConnectionResponse);
function GetString(const AIndex: Integer): String;
begin
Assert(InRange(AIndex, 0, ARequest.QueryFields.Count - 1));
Result := HTTPDecode(ARequest.QueryFields[AIndex]);
end;
function GetInteger(const AIndex: Integer): Integer;
begin
Result := StrToInt(GetString(AIndex));
end;
function GetDouble(const AIndex: Integer): Double;
begin
Result := StrToFloat(GetString(AIndex));
end;
procedure AddString(const AValue: String);
begin
AResponse.Contents.Add(HTTPEncode(AValue));
end;
procedure AddDouble(const AValue: Double);
begin
AddString(FloatToStr(AValue));
end;
begin
try
AResponse.Code := 200;
ARequest.QueryFields.StrictDelimiter := True;
ARequest.QueryFields.Delimiter := '&';
ARequest.QueryFields.DelimitedText := ARequest.QueryString;
case LowerCase(ParseURI(ARequest.URI).Document) of
'hello': begin
AddString('Hello ' + GetString(0) + '.');
end;
'copy': begin
AddString(Copy(GetString(0), GetInteger(1), GetInteger(2)));
end;
'readfiletostring': begin
AddString(ReadFileToString(GetString(0)));
end;
'uppercase': begin
AddString(UpperCase(GetString(0)));
end;
'lowercase': begin
AddString(LowerCase(GetString(0)));
end;
'sin': begin
AddDouble(Sin(GetDouble(0)));
end;
'cos': begin
AddDouble(Cos(GetDouble(0)));
end else begin
raise Exception.Create('Unknown Command');
end;
end;
except
on E: Exception do begin
AResponse.CodeText := E.Message;
AResponse.Code := 400;
end;
end;
end;
begin
try
with TMyRemotingServer.Create(nil) do begin
try
Port := 1234;
Active := True;
finally
Free;
end;
end;
except
on E: Exception do WriteLn(E.Message);
end;
ReadLn;
end.
Then on the client side, I code up a generic Http remote function which accepts an array of string, and returns an array of string. Using that function, I create local function wrappers for each of the remote functions.
program client;
{$mode objfpc}{$H+}
uses
Classes,
fphttpclient,
HttpDefs,
SysUtils,
Types,
Variants;
function HttpRemoteCall(const AName: String; const AParams: array of String;
const AExpectedCount: Integer): TStringDynArray;
var
LIndex: Integer;
begin
with TStringList.Create do begin
try
for LIndex := Low(AParams) to High(AParams) do begin
Add(HttpEncode(AParams[LIndex]));
end;
StrictDelimiter := True;
Delimiter := '&';
DelimitedText := TFPHTTPClient.SimpleGet('http://127.0.0.1:1234/' + AName + '?' + DelimitedText);
Assert(Count = AExpectedCount);
SetLength(Result, Count);
for LIndex := 0 to Count - 1 do begin
Result[LIndex] := HttpDecode(Strings[LIndex]);
end;
finally
Free;
end;
end;
end;
function HttpSin(const AValue: Double): Double;
begin
Result := StrToFloat(HttpRemoteCall('sin', [FloatToStr(AValue)], 1)[0]);
end;
function HttpCos(const AValue: Double): Double;
begin
Result := StrToFloat(HttpRemoteCall('cos', [FloatToStr(AValue)], 1)[0]);
end;
function HttpReadFileToString(const AFileName: String): String;
begin
Result := HttpRemoteCall('readfiletostring', [AFileName], 1)[0];
end;
function HttpHello(const AString: String): String;
begin
Result := HttpRemoteCall('hello', [AString], 1)[0];
end;
function HttpCopy(const AString: String; const APos, ALen: Integer): String;
begin
Result := HttpRemoteCall('copy', [AString, IntToStr(APos), IntToStr(ALen)], 1)[0];
end;
begin
try
WriteLn(HttpReadFileToString('C:\Documents and Settings\Admin\Desktop\Remoting\server.lpr'));
WriteLn(HttpHello('World'));
WriteLn('Sin(1.0)=', HttpSin(1.0));
WriteLn('Cos(0.5)=', HttpCos(0.5));
WriteLn('Copy=', HttpCopy('Lazarus is Fantasic!', 1, 7));
except
on E: Exception do WriteLn(E.Message);
end;
ReadLn;
end.
I got pretty good results using this method.
Note: Obviously, the ideal situation would be to have all your remoted API in a unit, and written in such a way that the local "client" code doesn't even look like it is using a remoted API.