DataSnap 2010 – sending and receiving objects
If you start to use or migrate your application to the new DataSnap you may ask how to transfer objects between client/server. The data types allowed to be transfered in DataSnap 2009 is limited to the dbExpress data types, but in DataSnap 2010 part of Delphi 2010 you are allowed to transfer any kind of object.
DataSnap 2010 brings support for JSON (JavaScript Object Notation), which is a lightweight data-interchange format, easy for humans to read/write and easy for machines to parse and generate, also independent of language. In a future post I will expand on JSON advantages. To start I will explain how to transfer an object between DataSnap Win32 client and server.
Let’s define the object we would like to transfer, class TCustomer.
unit Customer; interface uses DBXJSON, DBXJSONReflect, SysUtils; type TMaritalStatus = (msMarried, msEngaged, msEligible); TCustomer = class private FName: string; FAge: integer; FMaritalStatus: TMaritalStatus; public property Name: string read FName write FName; property Age: integer read FAge write FAge; property MaritalStatus: TMaritalStatus read FMaritalStatus write FMaritalStatus; function toString : string;override; end;
Only objects that descend from TJSONObject are able to be transferred between client and server without any transformation in DataSnap 2010. If your object does not descend from TJSONObject, then you have to use the TJSONMarshal and TJSONUnMarshal classes to convert those objects. The example below shows how to make this conversion.
unit Customer; function CustomerToJSON(customer: TCustomer): TJSONValue; var m: TJSONMarshal; begin if Assigned(customer) then begin m := TJSONMarshal.Create(TJSONConverter.Create); try exit(m.Marshal(customer)) finally m.Free; end; end else exit(TJSONNull.Create); end; function JSONToCustomer(json: TJSONValue): TCustomer; var unm: TJSONUnMarshal; begin if json is TJSONNull then exit(nil); unm := TJSONUnMarshal.Create; try exit(unm.Unmarshal(json) as TCustomer) finally unm.Free; end; end;
You don’t need to implement two transformation methods for every single class, you can implement a generic method for class that use simple data types, I mean strings, numbers, boolean. Due to the fact that some support classes can be quite complex and some types are fully supported by the current RTTI runtime converters can be added. I will not focus on converters, instead I recommend you to read this post from Adrian Andrei our RAD Studio Database Architect.
At this point we have the TCustomer class ready to cross between client and server, in this case now we just need to implement a Server Method which return a TJSONValue after the TCustomer transformation, like the example below.
// protected function TServerMethods.GetCustomer: TCustomer; begin Result := TCustomer.Create; Result.Name := 'Pedro'; Result.Age := 30; Result.MaritalStatus := msEligible; end; // public function TServerMethods.GetJSONCustomer(): TJSONValue; var myCustomer: TCustomer; begin myCustomer := GetCustomer; Result := CustomerToJSON(myCustomer); myCustomer.Free; end;
Executing the method GetJSONCustomer from the client side will be necessary to convert the method return from TJSONValue to TCustomer, using the method JSONToCustomer.
var proxy: TServerMethodsClient; myJSONCustomer: TCustomer; begin try proxy := TServerMethodsClient.Create(SQLConnection1.DBXConnection); myJSONCustomer := JSONToCustomer(proxy.myJSONCustomer); Button1.Caption := myJSONCustomer.ToString; myJSONCustomer.Free; finally SQLConnection1.CloneConnection; proxy.Free; end; end;
Much more can be done, for example return an Array of objects, complex classes, etc. In future posts I will comment about that.
The source code is available for download
Thanks Andreano for this post.
I didn’t test this code, but by reading it I can’t figure out where the call to GetJSONCustomer is executed, from the client side.
I think the call should be in line 8 of the last code snippet:
myJSONCustomer := JSONToCustomer(proxy.GetJSONCustomer);
Leonardo.
I wonder if one of the design goals of Datasnap 2010 was to use as many fashionable buzzwords as possible.
Why Datasnap 2010 lacks an efficient binary transfer protocol? JSON was designed for a language that can’t handle binary data easily. Delphi can. JSON should be just one ot the transports when compatibiity with other technologies is important. But applications that need to move large amount of data among Delphi applications whu should go through inefficient string encodings? Otherwise it is easier to send a socket port, open the socket and perform a binary transfer. Even WCF understood it and offers a “binary XML” format where compatibility is not needed but performance are.
I guess I am not going to migrate our DCOM Datasnap applications to this technology until it starts to offer everything DCOM offers. Or I will migrate to another RPC technology that would allow us to buy Delphi Professional instead of the Enterprise. It would be a huge saving – given the higher SKU is now offering only low-end solutions.
if my object has a TStream property will the above code work ?
Yes, TStream is part of the data types supported by DataSnap
Quando eu não quero retornar objeto nenhum e retorno nil no servidor, as classes proxy geram uma exceção de que não pôde converter TJSONNull em TJSONObject.. isso é normal? o que eu posso fazer para retornar null ou tjsonnulll ?
por exemplo uma consulta de um cliente que nao existe.
Obrigado!
@Paulo, quando o retorno for null testa algo assim if not Assigned(Objeto) then ….