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

Categories:DelphiTags: , ,

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

Posts relacionados

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

  1. Responder Fabio Monego Puhl says:

    Olá, sempre ótimos os seus post, mas neste caso tenho uma duvida como o Marshal se comporta para um objeto com uma lista interna, ou como fazer um objeto que o Marshal consiga funcionar a contento?

  2. Responder Andreano Lanusse says:

    Oi Fabio, obrigado.

    Se você tem uma lista simples (Array), irá usar passar um JSONArray. De uma lida neste outro post http://www.andreanolanusse.com/pt/datasnap-2010-enviando-e-recebendo-array-de-strings-numbers-e-outros-tipos/

  3. Responder 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?

  4. Responder Hugo says:

    Andreano, boa tarde…

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

  5. Responder Andreano Lanusse says:

    Oi Hugo não seria diferente, da uma lida nesse artigo aqui do meu amigo Malcolm Groves http://www.malcolmgroves.com/blog/?p=420

  6. Responder 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 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.

  7. Responder 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 says:

      Oi Claudio,

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

  8. Responder 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.

  9. Responder Isaque Pinheiro says:

    Heheheheeh, Andreano, estava procurando EXATAMENTE como converter aquela class do post “DataSnap 2010 – Enviando e Recebendo objetos”, em generic, ou seja me deus mastigado, brigadão.

  10. Responder 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.

  11. Responder 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!

  12. Responder 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.

  13. Responder 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.

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>