Utilizando generics para transformação genérica de Objetos em DataSnap 2010

No meu post anterior “DataSnap 2010 – Enviando e Recebendo objetos” mostrei como transferir objetos através de JSON objects, nos comentários relacionados a este post Rafael Soares fez uma interessante observação com relação a necessidade de se ter que escrever em toda a classes os métodos de conversão de JSON para TObject e TObject para JSON. Na verdade isso não é necessário, você pode utilizar generics e assim ter apenas dois métodos de conversão para qualquer classe.

O exemplo utilizado no post anterior o qual os fontes foram disponibilizados para download, traz a classe TCustomer como exemplo e implementa os método JSONtoCustomer e CustomerToJSON, este fazem referência direta a classe TCustomer, assim sendo fiz algumas modificações onde nos permitirá utilizar estes dois métodos para qualquer classe, primeiro os métodos foram renomeados para JSONtoObject e ObjectToJSON e adicionados a uma nova classe chamada TBaseObject, este métodos foram definidos como class function, desta forma não precisamos instanciar o objeto. Agora TCustomer passa a herdar desta, assim sendo qualquer objeto terá este método implemetado.

Os métodos da classe TBaseObject agora definem <T> como parâmetro de entrada e retorno, ou seja, o tipo que for definido durante a utilização do método é o que será considerado pelo método, aqui estamos utilizando Generics.

unit BaseObject;

interface

uses DBXJSON, DBXJSONReflect;

type
  TBaseObject = class
  public
    { public declarations }
    class function ObjectToJSON<T : class>(myObject: T): TJSONValue;
    class function JSONToObject<T : class>(json: TJSONValue): T;
  end;

implementation

{ TBaseObject }

class function TBaseObject.JSONToObject<T>(json: TJSONValue): T;
var
  unm: TJSONUnMarshal;
begin
  if json is TJSONNull then
    exit(nil);
  unm := TJSONUnMarshal.Create;
  try
    exit(T(unm.Unmarshal(json)))
  finally
    unm.Free;
  end;

end;

class function TBaseObject.ObjectToJSON<T>(myObject: T): TJSONValue;
var
  m: TJSONMarshal;
begin

  if Assigned(myObject) then
  begin
    m := TJSONMarshal.Create(TJSONConverter.Create);
    try
      exit(m.Marshal(myObject));
    finally
      m.Free;
    end;
  end
  else
    exit(TJSONNull.Create);

end;

end.

Já a classe TCustomer herda de TBaseObject.

unit Customer;

interface

uses
   DBXJSON, DBXJSONReflect, SysUtils, BaseObject;

type
   TMaritalStatus = (msMarried, msEngaged, msEligible);

TCustomer = class(TBaseObject)
    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;

Agora todos os Server Methods da classe TDSServerMethods passam a utilizar os métodos de conversão de objetos especificando qual o tipo que deverá ser utilizado. O exemplo abaixo retorna uma lista de TJSONArray, cada elemento adicionado ao Array usa a sintaxe TCustomer.ObjectToJSON<TCustomer>.

<TCustomer> define que o tipo do Objeto que está sendo adicionado ao Array.

function TDSServerMethods.ListofCustomer: TJSONArray;
var
  i: Integer;
  myCustomer: TCustomer;
begin
  Result := TJSONArray.Create;

  for i := 0 to 19 do
  begin
    myCustomer := GetCustomer;
    myCustomer.Name := 'Customer ' + IntToStr(i);
    myCustomer.Age := i;
    Result.AddElement(myCustomer.ObjectToJSON<TCustomer>(myCustomer));
  end;
end;

Para ler o retorno no lado Client utilizamos o mesmo princípio com Generics, onde cada item é transformado de JSON para TCustomer conforme o exemplo abaixo:

var
  proxy: TDSServerMethodsClient;
  mySingleCustomer: TCustomer;
  allCustomers: TJSONArray;
  i: Integer;
begin

  proxy := nil;
  try
    proxy := TDSServerMethodsClient.Create
      (DMClientContainer.MyDSServer.DBXConnection);
    allCustomers := proxy.ListofCustomer;

    for i := 0 to allCustomers.Size - 1 do
    begin
      mySingleCustomer := TCustomer.JSONToObject<TCustomer>(allCustomers.Get(i));
      MMLog.Lines.Add(mySingleCustomer.ToString);
      mySingleCustomer.Free;
    end;

  finally
    proxy.Free;
  end;

Vimos aqui como Generics nos auxilia na transformação de objetos em DataSnap.

O exemplo atualizado está disponível no CodeCentral – Download do código fonte

16 respostas
  1. José Abílio
    José Abílio says:

    Olá Adreano, muito interessante o seu post, mas completando a pergunta do Fabio, se por ventura a lista for uma lista de objetos, o que devo fazer?

    Responder
  2. Hugo
    Hugo says:

    Andreano, boa tarde…

    Supomos que eu queira trabalhar com INTERFACE (IBaseObject) no lugar da classe. Neste caso como utilizar Generics com INTERFACE?

    Responder
  3. Claudio
    Claudio says:

    Andreano, pensando em uma conexao remota utilizando DataSnap, onde meu servidor fica em outro local e acesso meus metodos online, queria saber se a velocidade na hora de trazer uma lista de clientes por exemplo é maior que utilizando o ClientDataSet?

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      Olá Claudio,

      Eu diria que não seria percepitiva a diferente, se você for usar só server method e deixar o clientdataset de lado, vai perder uma produtividade enorme no desenvolvimento e todos os benefícios que o ClientDataSet traz para o desenvolvimento RAD.

      Tem que prevalecer o bom senso, para o que seja dataware recomendo manter o ClientDataSet.

      Responder
  4. Claudio
    Claudio says:

    Andreano, tenho a seguinte duvida, trabalhando em um ambiente datasnap vou ter meu SqlDataSets e Provider no lado servidor e meus ClientDataSets no lado cliente, estou me perdendo um pouco na hora de trabalhar desta maneira.
    Como seria uma boa pratica para esse metodo de trabalho?

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      Oi Claudio,

      Perdendo o que? Só vejo benefícios utilizando SqlDataSet + Provider + ClientDataSet, esta é a melhor forma de se trabalhar de forma RAD.

      Responder
  5. Claudio
    Claudio says:

    Andreano, na verdade tambem nao tenho duvidas sobre os beneficios, na verdade minha duvida é no processo mesmo.
    Acredito que seja assim; tenho uma tabela de clientes e uma de produto. Acho que a melhor solucao seria um ServerMethod para cada tabela.
    No lado servidor ficaria assim:
    function TDSServer.ListarClientesporCodigo(pCodCli: string): Boolean;
    begin
    QryCadCli.Active := False;
    QryCadCli.CommandText := ‘Select * From CADCLI Where CODCLI = ‘+
    QuotedStr(pCodCli);
    QryCadCli.Active := True;
    end;

    E no lado cliente ficaria assim:
    procedure TFrmCliente.Button5Click(Sender: TObject);
    var
    proxy : TDSServerClient;
    begin
    proxy := TDSServerClient.Create(DSServerCliente.Conexao.DBXConnection);
    proxy.ListarClientesporCodigo(Edit1.Text);

    DSServerCliente.CdsCadCli.Active := True;
    end;

    Minha duvida é se esse processo esta correto.

    Responder
  6. Nelson Lima
    Nelson Lima says:

    Andreano minha dúvida é como serializar o Delta ClientDataSet que é um OleVariant. Fiz uma classe TDeltaPack com o Delta e o ProviderName. Tenho que passar uma array disso para o servidor de aplicação consigo passar o array o ProviderName que é string mas o Delta não. Como devo proceder isso é fundamental para as minhas aplicações.

    Responder
  7. Júlio César Ferreira (@jcmferreira)
    Júlio César Ferreira (@jcmferreira) says:

    Esse recurso de transferência de objetos com o JSON é extremamente interessante.

    Mas, se existe uma forma de retornarmos uma classe qualquer através do Server Methods, sendo ela descendente de TObject, qual seria a real necessidade dessa nova implementação?

    Desculpe pela pergunta mas, o JSON se torna um padrão mais correto e ideal para a transferência de objetos ou apenas mais uma possibilidade?

    Blog nota 10!

    Responder
  8. Arimateia JR
    Arimateia JR says:

    Olá Andreano. Parabéns pelo post. Venho aqui esclarecer a seguinte dúvida: tentei usar o método JSONToObject diretamente através da classe TBaseObject, pois o método é estático. Porém dá um “Internal Error” ao tentar fazer o UnMarshal. Observei esse erro, pois as minhas classes de negócio já herdam de outras, então tive que implementar chamando diretamente TBaseObject.JSONToObject. Você poderia me explicar pq está dando esse erro? Obrigado.

    Responder
  9. Arimateia JR
    Arimateia JR says:

    Andreano, acabei de conseguir contornar esse problema. No método JSONToObject, adicionei a constraint “constructor” e funcionou tranquilo. Ficou assim: class function JSONToObject(json: TJSONValue): T;

    Agora estou conseguindo transformar as mensagens JSON em objetos de negócio assim: obj := TBaseObject.JSONToObject;

    Porém, mudei o nome da classe TBaseObject para TFuncoesJSON. Acredito que assim traduz melhor o real objetivo dessa classe.

    Valeu.

    Responder

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *


Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.