DataSnap 2010 – sending and receiving objects

Let's share the knowledge with your friends

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


Let's share the knowledge with your friends
7 replies
  1. Leonardo M. Ramé
    Leonardo M. Ramé says:

    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.

    Reply
  2. Luigi D. Sandon
    Luigi D. Sandon says:

    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.

    Reply
  3. Paulo Alexanre
    Paulo Alexanre says:

    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!

    Reply

Trackbacks & Pingbacks

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.