DataSnap 2010 – Enviando e recebendo objetos

Uma das perguntas frequentes dos usuários de Delphi 2009 e que utilizam DataSnap para criação de objetos é sobre a transferência de objetos entre cliente e servidor. No DataSnap 2009 estavamos limitados aos data types do dbxExpress, agora com o DataSnap 2010 que acompanha o Delphi 2010, isso é totalmente possível.

DataSnap 2010 traz o suporte a JSON (JavaScript Object Notation) que é uma formatação leve de troca de dados, totalmente independente de linguagem, futuramente vou comentar mais sobre JSON e suas vantagens, para começar este post irá mostrar como transferir objetos entre cliente e servidores DataSnap, sendo que ambos cliente e servidor são aplicações Delphi.

Para começar vamos definir uma classe chamada 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;

Para transferir objetos em DataSnap estes devem descender the TJSONObject, no caso de não ser um objeto descendente você terá que utilizar as classes TJSONMarshal e TJSONUnMarshal para efetuar a transformação dos objetos. Sendo assim os métodos abaixo irão efetuar a conversão dos mesmos.

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;

Com isso temos classe TCustomer pronta para trafegar entre cliente e servidor, assim sendo basta implementar um Server Method que retorne um TJSONValue a partir da transformação de TCustomer, como o exemplo abaixo.

// 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;

No lado cliente ao executar o método GetJSONCustomer será necessário efetuar a transformação de TJSONValue para TCustomer, utilizando o método 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;

Muito mais pode ser feito, como retornar Arrays de objetos, classes mais complexas, etc. Estarei abordando estes temas em futuros posts.

Download do código fonte

54 respostas
  1. Everson Novka
    Everson Novka says:

    Olá Andreano, bem interessante esses novos recursos do Delphi 2010, a minha licença do novo Delphi deve estar chegando nessa semana, já estou aqui pirando para poder escrever meus serviços com datasnap. Alguns dias atrás eu escrevi um mini-artigo sobre exportação de dados de um dataset para um arquivo JSON utilizando versões anteriores ao Delphi 2010, utilizei a biblioteca lkjson, q é opensource. Se tiver interesse, segue meu blog: http://www.eversonnovka.com

    []’s

    Responder
  2. Marcio Costa
    Marcio Costa says:

    Caro Andreano,

    Ótimo post parabéns.

    Uma pequena dúvida: Há algo pronto, no delphi 2010, para enviar/receber objetos mais complexos como por exemplo um TClientDataSet?
    Obrigado.

    Responder
  3. Bruno
    Bruno says:

    Muito bom o artigo, estou querendo fazer algo parecido com um Browser, onde todos os meus formulários ficariam do lado do Servidor e eu teria no lado Cliente somente uma carcaça para ler esses objetos do Servidor e traduzir. Acho que isso é possível usando o JSON, vou pesquisar mais, de qq forma obrigado.

    Responder
  4. Rafael Soares
    Rafael Soares says:

    Boa tarde,
    Teria algum exemplo para a conversão genérica de objetos para JSON?
    Escrever dois métodos para para cada classe não me parece uma boa coisa. A conversão genérica de um objeto para JSON eu consegui, agora de JSON para um objeto está difícil…

    Responder
  5. Andreano Lanusse
    Andreano Lanusse says:

    Rafael, agora não tem um método genérico, mas você pode criar um utilizando generics 🙂

    Para as próximas versões isso será transparente, mas você também terá a opção de controlar como converter.

    Responder
  6. Andreano Lanusse
    Andreano Lanusse says:

    Oi Eduardo,

    Na minha opinião não faz sentido, querer transferir TClientDataSet é a mesma coisa que transferir dados e isso ele já faz com o Provider, no caso de TSQLQuery é querer passar SQL isso você faz pelo ClientDataSet, server method ou outra forma.

    Responder
  7. José Abílio
    José Abílio says:

    Adreano,
    Novamente lhe dando os parabéns pelo ótimo post, mas estou uma dúvida, terei que ter as classes tanto na aplicação cliente quanto na aplicação servidora?
    Isso não seria um retrabalho?
    Ter que refazer toda a estrutura de classes novamente ou no cliente ou no servidor.
    Por exemplo, aqui tenho uma série de classes com toda as regras de negócios e mapeamento objeto relacional, vou ter que fazer isso tudo denovo no servidor, ou tem algum jeito de fazer as regras de negócio no servidor com as classes que já tenho e no cliente apenas chamar elas, de algum modo sem ter que reescrever boa partes delas?

    Responder
  8. Juliano
    Juliano says:

    Ola Andreano.
    Primeiramente parabens pelo blog, muito bom msm.

    Seguinte fiz uma implementacao neste genero utilizando as dicas do outro artigo pra deixar a conversao generica, mas estou com uma dúvida.

    Como fica o gerenciamento da memoria no server, exemplo: Eu faco uma requisicao de uma lista, é feita a consulta instancia um JSonArray seta e devolve para a interface, mas a memoria ocupada por esta lista no server continua lah.

    Tem alguma dica.

    Mais uma vez parabéns.

    Responder
  9. Juliano
    Juliano says:

    Certo, mas como faço para destruir os objetos do server, visto que só poderei destruir eles apos a conclusão da solicitação no entanto quando a solicitação é concluída nao tenho mais acesso as objetos instanciado, já que a execução retornou para o cliente.

    Abraço

    Responder
  10. Silvio Mendes
    Silvio Mendes says:

    Muito bom mesmo esse post sobre datasnap, ainda estou me iniciando nisso mas ele demonstra o poder e a simplicidade de uso. Outro ponto é o uso do JSON, que já conhecia, mas no EXTJS que é todo baseado nessa notação.

    Com toda certeza terei perguntas, mas vou explorar mais os códigos aqui.

    Parabéns pelas dicas.

    Responder
  11. Paulo
    Paulo says:

    Exemplo fantastico do uso do DATASNAP, me ajudou muito na migração de uma sistema feio em delphi 2009.

    mas gostaria de saber como enviar e receber dados de um campo BLOB do banco de dados, eu até recebo mas nao consigo enviar. o sistema sempre reclama falando que o JSON nao suporta ARRAY e nem DATA.

    poderia passar um exemplo de como trabalhar com eses tipo de Dado usando o JSON e D2010 ?

    obrigado

    Responder
  12. Cleyton
    Cleyton says:

    Olá Andreano, primeiramente otimo post, muito valido para mim.

    Eu gostaria de saber se é possivel fazer o inverso enviar um JSON para o Servidor, usando REST, estou iniciando em REST, ta ficando interessando, só que hoje tenho a necessidade de retornar um Objeto para o Servidor, e até o momento só descobri como passar os parametros e no formato String … por exemplo …

    http://127.0.0.1:8081/datasnap/rest/TSMTeste/GetTeste/AquiOParametro

    no resto mais uma vez parabéns, não somente por esse post, e também pelos outros.

    Obrigado.

    Responder
  13. Cleiton
    Cleiton says:

    Andreano,

    parabéns pelo blog, sem dúvida alguma essas curtas postagem estão trazendo grandes resultados…hehehe
    Assim quanto a isso eu só tenho a agradecer, pois é graças aos posts como este que nós programadores podemos estar aprendendo, nos atualizando e etc…

    Bom, baseado neste teu tópico, eu fui tentar criar um exemplo, simples, claro, mas eu fiz mais para aprender. Eu criei um sistema de classes para fazer vendas/controle de estoque. minha idéia, o cliente quando quer fazer uma venda, ele chama uma função no servidor, e passa uma classe (TVenda) com as informações para o servidor proceder. Desse jeito eu consigo ter um controle de estoque bem consciente (controlando a concorrência…) bom mas o problema, foi que os produtos eu representei com uma classe TProduto, e a classe TVenda possuia uma TList , algo digamos, bem intuitivo…mas, na conversão de objetos JSON, essa lista, literalmente não converte. Eu não consegui encontrar alguma solução, por acaso você teria alguma idéia a respeito disso???

    Muito obrigado, pela atenção…

    Responder
  14. Marcelo
    Marcelo says:

    Boa tarde Andreano

    Estou tendo o mesmo problema que o otto mencionou ao transportar um objeto que possui uma property do tipo double.

    Poderia me dar alguma dica de como resolver esse problema?

    Responder
  15. Marcelo
    Marcelo says:

    Andreano

    Obrigado pela resposta utilizo o Delphi 2010 apesar de não ser indicado a mudança nos fontes da vcl resolvi o problema seguindo o exemplo desse poste http://www.expressolivre.net/openmail/index.php?msg_id=%22123336%22 e funcionol perfeitamente.

    Agora estou com problema ao rodar a aplicação servidora em uma máquina com windows server 2003 sem farewall, testei em outras máquinas e funciona, na verdade não sei se a falta do firewall é o problema, mas, é o que difere das outras máquinas que testei.

    Responder
  16. Alexandre José
    Alexandre José says:

    No meu caso gostaria de commitar vários CDS dentro de uma mesma transação, passando vários clients em um JSONArray. Irei tentar fazer isso via DBXReader. Eu utilizo isso com RemObjects, queria converter para o novo Datasnap. =)

    Obrigado!

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      Oi Alexandre, você pode ter uma única transação ao fazer o applyupdates de vários ClientDataSet se eles tiverem conectados, da uma olhada em nested clientdataset

      Responder
  17. Cleyton
    Cleyton says:

    Ok Andreano, vou explicar melhor …

    Estou desenvolvendo um sistema, que tem um servidor DataSnap com Rest, até ai tudo bem, com seus exemplos e o dos amigos, consegui desenvolver sem problema, inclusive se comunicando com outras linguagens, por exemplo Flex.
    Criei minha regra de negocio no meu servidor DataSnap, e utilizando HTTPService no Flex consigo pegar os dados do meu Servidor e popular os meus campos no Flex.
    {
    var ht:HTTPService = new HTTPService();
    ht.url = “http://localhost:8081/datasnap/rest/TSMClient/GetClient/” + txtConsulta.text;
    }
    seria mais ou menos assim, passo por parâmetro o txtConsulta e ele retorna o que eu quero.
    Só que surgiu a necessidade de passar um arquivo(bytes) = Upload, por exemplo, uma imagem do flex para o meu Servidor DataSnap, ai que está o meu problema, pq no caso desse exemplo que passei eu passo do Flex para o DataSnap como parametro um valor String, em outras linguagens, como por exemplo ASP.NET é utilizado Request para fazer essa operação, só que não encontrei nada parecido nos componentes DataSnap que possa me permitir fazer esse upload … realmente estava confuso anteriormente espero que tenha ficado mais claro … obrigado pelo retorno.

    Responder
  18. Dannyrooh
    Dannyrooh says:

    Boa tarde,

    Criei um servidor datasnap que retorna uma lista de TCustomer agrupada com JsonArray e JsonValue, estou tentando consumir o resultado numa aplicação asp.net com delphi prism, ao gerar o proxy ele retorna a classe como object, alguma forma de realizar a transformação direto com .net ?

    Responder
  19. Rodrigo
    Rodrigo says:

    Excelente post, me ajudou muito, porém tenho um problema, já citado acima. Criei uma classe que possui property do tipo Double e me ocorre um erro na conversão do objeto, impossibilitando a transferência do objeto para o cliente. Como posso resolver o problema ?

    Att,

    Responder
  20. Francis Silva
    Francis Silva says:

    Eu consegui resolver o problema de transporte de datas e doubles entre o servidor DataSnap e a aplicação cliente utilizando um truque muito simples. Se você observar a estrutura de uma classe qualquer em formato JSON vai notar que o que é mapeado para o JSON não são as propriedades da classe em sí, mas sim as suas variáveis privadas. Sendo assim consegui resolver o problema som o seguinte truque:

    TCliente = Class (TObject)
    private
    FID: Integer;
    FNome: String;
    FDataCadastro: String;
    FValor: String;
    function GetDataCadastro: TDateTime;
    procedure SetDataCadastro(value: TDateTime);
    function GetValor: Double;
    procedure SetValor(value: Double);
    ….

    public
    property DataCadastro: TDateTime read GetDataCadastro write SetDataCadastro;
    property Valor: Double read GetValor write SetValor;
    ….
    end;

    O truque é na implementação dos métodos privados:

    function TCliente.GetDataCadastro: TDateTime;
    begin
    Result := StrToDateTime(FDataCadastro); // Vai bem alguma checagem extra aqui antes da conversão
    end;

    function TCliente.SetDataCadastro(value: TDateTime);
    begin
    FDataCadastro:= FormatDateTime(‘DD/MM/YYYY HH:NN:SS’, Value);
    end;

    function TCliente.GetValor: Double;
    begin
    Result := StrToFloatDef(FValor,0);
    end;

    function TCliente.SetValor(value: Double);
    begin
    FValor := FloatToStr(value);
    end;

    Dessa forma quando você atribuir ou obter um valor das propriedades DataCadastro e Valor, automaticamente eles serão convertidos de TDateTime e Double para String, e virce-versa. Como o JSON só armazena as informações dos campos privados das propriedades, as informações são armazenas como String e não há nenhum problema na conversão de volta na aplicação cliente.

    Espero ter ajudado.

    Responder
  21. Braga
    Braga says:

    Olá Sr. Andreano Lanusse! Parabéns pela execelência de seu trabalho. Embora tenha procurado em várias fontes, não encontrei nenhum material editado sobre “Como construir uma aplicação do começo ao fim” com DataSnap, em 3 camandas.
    Se poder me ajudar com qualquer informação de onde e como encontrar, serei imensamente grato.

    Responder
  22. Nelson Lima
    Nelson Lima says:

    Prezado Andreano,

    Eu gostaria de criar metodo remoto onde eu possa passar um array de Delta do ClientDataSet + seu ProviderName

    Para isso criei um class TDeltaApply Tentei TDeltaApplyArray : array of TDeltaApply e ele não aceita como metodo remoto esse tipo.

    Então como posso para meu servidor uma array de delta e seu providername????

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

    Boa noite Andreano!

    Estava estudando esse seu exemplo de transferência de objetos do servidor para o cliente e surgiu uma dúvida: Utilizando esse seu exemplo da classe TCustomer, poderíamos ter uma propriedade public do tipo TJPEGImage ou TBitmap, algo como TCustomer.Photo?

    Meu servidor iria definir o conteúdo dessa propriedade e passar o objeto via JSON para o cliente e o mesmo conseguir exibir em um TImage padrão, a imagem que está na propriedade Photo?

    Responder
  24. William
    William says:

    Testem direito esse exemplo. O método toString que ele chama é da classe tcustomer que foi compilada dentro do executável do cliente através da declaração no uses, em nenhum momento foi executado o tostring do servidor datasnap.

    Só alterar o método tostring do tcustomer assim:

    function TCustomer.ToString: string;
    begin
    Result := Self.Name + ‘ – Age: ‘ + IntToStr(Self.Age)+’ teste’;
    end;

    e compilar o server e não compilar o cliente, vai ver que o retorno será sem a string ‘teste’ que foi colocado no método.

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      @William,

      Infelizmente você não entendeu o exemplo, o qual mostra o envio e recebimento de objetos no DataSnap.

      TCustomer é o objeto usado como exemplo, e no caso ele é usado no server e client. Para representar e ter acesso ao TCustomer no cliente você TEM que ter a estrutura do mesmo no cliente.

      O método ToString somente acessa a propriedade do objeto e não faz referência nenhum ao server.

      Responder
  25. Magnaldo Melo
    Magnaldo Melo says:

    Andreano,

    Muito tem se falado a respeito dos objetos que ficam na memória, o que acaba sendo uma bomba relógio.
    Eu utilizo o DataSnap (Rest + Json). Há remover esses objetos da memória?

    Responder
    • Andreano Lanusse
      Andreano Lanusse says:

      Oi Magnaldo, tem sim, tudo depende como você configura o lifecycle dos objetos através do DSServerClass, aqui no blog tem artigos sobre isso

      Responder
  26. Edner
    Edner says:

    Prezado Andreano, Tudo bem?

    Excelente post. Estou iniciando no desenvolvimento com DataSnap Rest Application, e me surgiu uma dúvida. Implementei um servidor que me retorna uma lista de DataSets (TFDJSONDataSets). Utilizei um exemplo disponibilizado na documentação da embarcadero, conforme segue abaixo:

    FDQuery.Close;

    Result := TFDJSONDataSets.Create;

    TFDJSONDataSetsWriter.ListAdd(Result, FDQuery);

    Após o fechamento da Query, eu ajusto o meu SQL e definindo os parâmetros para o mesmo.

    Pergunta: Devo abrir minha Query antes de retorná-lá?

    Fiz um teste, deixando com está, funcionou perfeitamente, mas apenas para um retorno de poucos registros, percebi que, se minha consultar retornar uma quantidade de informações tais como 50 registros, o dataset chega vazio no lado do cliente, como se não tivesse dados a ser retornado.

    Quando eu abro a query no lado do servidor, os registros retornam sem problema algum. O que estou fazendo de errado? tenho que configurar alguma informação que defina o tamanho das informações que devem ser trafegadas?

    Desde já agradeço a atenção

    Responder
  27. Francisco Barbosa
    Francisco Barbosa says:

    Boa Tarde, Adreano Lanusse

    Posts com qualidade assim só encontro aqui no seu site, muito interessante e tem me ajudado bastante. Percebi que não é uma dúvida só minha, então resolvi perguntar. É possível transferir arquivos exibindo uma barra de status, tipo um download. o cliente solicitar um arquivo presente no servidor datasnap e mostrar o progresso da transferencia com uma progressbar, é possível?

    Responder

Trackbacks & Pingbacks

  1. […] http://www.andreanolanusse.com/pt/datasnap-2010-enviando-e-recebendo-objetos This entry was posted on Thursday, July 15th, 2010 at 11:34 pm. You can follow any responses to this entry through the RSS 2.0 feed. Responses are currently closed, but you can trackback from your own site. […]

  2. […] 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 […]

  3. […] sequência ao tema transferência de objetos com DataSnap o qual iniciei escrevendo sobre a transferência de um simples objeto, e depois sobre o envio de array objetos, agora vamos descrever como enviar array que contenha […]

  4. […] sequência ao tema transferência de objetos com DataSnap o qual iniciei escrevendo sobre a transferência de um simples objeto, o qual é pré-requisito para entender este artigo, neste post descrevo como transferir uma lista […]

  5. […] Setembro 25, 2009 por marlonsouza Agora, no Delphi 2010, é possível transferir objetos entre entre clientes e servidores DataSnap. Leia mais a respeito no endereco http://www.andreanolanusse.com/pt/datasnap-2010-enviando-e-recebendo-objetos/ […]

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe um comentário

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.