Estava trabalhando para simplificar o TRESTClient do DataSnap  e implementar alguns recursos que gostaria de ter e não encontrei no componente distribuido com o delphi ( Package para TRESTSocialClient ). Mapear as respostas Array para um TFDDataset primordial para facilitar o desenvolvimento (ex: TRESTSocialClientDataset). E quando não é possível mapear para um Dataset… código+código+bug+complexo…

Quando trabalho com um Servidor REST Datasnap escrito para delphi – a integração é simples utilizando o “Wizard” para criar um projeto REST Client disponível. Quando o servidor REST responde para múltiplas plataformas é imperativo que o servidor envie respostas “padronizadas” que sejam compatíveis com a plataforma que o cliente utiliza. O caminho mais rápido é responder JSON padrão evitando respostas específicas de uma plataforma ou outra.

Exemplo: um método não pode responder com um TDataset.Data como é o indicado pelo Delphi… isto obrigaria que o cliente seja igualmente um Delphi.. Então para manter padrão o método deve responder com um TJSONValue ou TJSONObject.

Tomando por base o Servidor Datasnap do post “FireDAC – Datasnap” onde publica o método  GetCliente/{cnpj} (http://localhost:8080/datasnap/rest/TServerMethods1/GetCliente/123456) que retorna:

[code lang=”JSON”]
{"result":[{"cliente":[{"RowId":1,"codigo":1,"nome":"Embarcadero SA","cidade":"Sao Paulo","estado":"SP","endereco":"Rua…xxxx…,10","debitos":100000.12},{"RowId":2,"codigo":2,"nome":"Embarcadero SA2","cidade":"Sao Paulo","estado":"SP","endereco":"Rua…xxxx…,10","debitos":100000.12}],"adicional":[{"codigo":1,"nome":"Exemplo 2"}]}]}
[/code]

Nas andanças pelos BLOGs de MVPs encontrei um projeto “Introducing JsonToDelphiClass” que aponta para o repositório com o código do projeto.

O projeto JsonToDelphiClass permite que a representação JSON de retorno do servidor DataSnap seja colado no espaço para texto indicada e usar o botão de geração da Unit contendo a classe Delphi para a representação JSON.

As classes geradas pelo projeto possuem um método que fornecendo o código de retorno do servidor – popula o objeto delphi – facilitando a conversão da estrutura JSON para Classe.

class function FromJsonString(AJsonString: string): T... (criar a classe e popula)

.

Passando aquele retorno do Servidor, veja como ele construiu as classes para a representação JSON:

[code lang=”pascal”]
unit JsonRestServer;

// *************************************************
// Generated By: JsonToDelphiClass – 0.65
// Project link: https://github.com/PKGeorgiev/Delphi-JsonToDelphiClass
// Generated On: 2016-04-10 15:41:06
// *************************************************
// Created By : Petar Georgiev – 2014
// WebSite : http://pgeorgiev.com
// *************************************************

interface

uses Generics.Collections, Rest.Json;

type

TAdicionalClass = class
private
FCodigo: Extended;
FNome: String;
public
property codigo: Extended read FCodigo write FCodigo;
property nome: String read FNome write FNome;
function ToJsonString: string;
class function FromJsonString(AJsonString: string): TAdicionalClass;
end;

TClienteClass = class
private
FRowId: Extended;
FCidade: String;
FCodigo: Extended;
FDebitos: Extended;
FEndereco: String;
FEstado: String;
FNome: String;
public
property RowId: Extended read FRowId write FRowId;
property cidade: String read FCidade write FCidade;
property codigo: Extended read FCodigo write FCodigo;
property debitos: Extended read FDebitos write FDebitos;
property endereco: String read FEndereco write FEndereco;
property estado: String read FEstado write FEstado;
property nome: String read FNome write FNome;
function ToJsonString: string;
class function FromJsonString(AJsonString: string): TClienteClass;
end;

TResultClass = class
private
FAdicional: TArray<TAdicionalClass>;
FCliente: TArray<TClienteClass>;
public
property adicional: TArray<TAdicionalClass> read FAdicional write FAdicional;
property cliente: TArray<TClienteClass> read FCliente write FCliente;
destructor Destroy; override;
function ToJsonString: string;
class function FromJsonString(AJsonString: string): TResultClass;
end;

TRootClass = class
private
FResult: TArray<TResultClass>;
public
property result: TArray<TResultClass> read FResult write FResult;
destructor Destroy; override;
function ToJsonString: string;
class function FromJsonString(AJsonString: string): TRootClass;
end;

implementation

{TAdicionalClass}

function TAdicionalClass.ToJsonString: string;
begin
result := TJson.ObjectToJsonString(self);
end;

class function TAdicionalClass.FromJsonString(AJsonString: string): TAdicionalClass;
begin
result := TJson.JsonToObject<TAdicionalClass>(AJsonString)
end;

{TClienteClass}

function TClienteClass.ToJsonString: string;
begin
result := TJson.ObjectToJsonString(self);
end;

class function TClienteClass.FromJsonString(AJsonString: string): TClienteClass;
begin
result := TJson.JsonToObject<TClienteClass>(AJsonString)
end;

{TResultClass}

destructor TResultClass.Destroy;
var
LclienteItem: TClienteClass;
LadicionalItem: TAdicionalClass;
begin

for LclienteItem in FCliente do
LclienteItem.free;
for LadicionalItem in FAdicional do
LadicionalItem.free;

inherited;
end;

function TResultClass.ToJsonString: string;
begin
result := TJson.ObjectToJsonString(self);
end;

class function TResultClass.FromJsonString(AJsonString: string): TResultClass;
begin
result := TJson.JsonToObject<TResultClass>(AJsonString)
end;

{TRootClass}

destructor TRootClass.Destroy;
var
LresultItem: TResultClass;
begin

for LresultItem in FResult do
LresultItem.free;

inherited;
end;

function TRootClass.ToJsonString: string;
begin
result := TJson.ObjectToJsonString(self);
end;

class function TRootClass.FromJsonString(AJsonString: string): TRootClass;
begin
result := TJson.JsonToObject<TRootClass>(AJsonString)
end;

end.

[/code]

 

 

 

O uso de Datasnap como cliente de acesso ao servidor JSON é resultante de uma combinação de componentes do delphi para completar a chamada. Se do lado do Servidor há um Datasnap então o lado cliente é facilitado ao coletar informações dos métodos exportados pelo servidor e geração do cliente automático. A implementação do servidor JSON pode apresentar uma variedade muito grande de formato de publicação (ou não) dos seus métodos, o que pode se tornar bastante trabalhoso a implementação do cliente.

Simplificando o trabalho:

TRESTSocialClient


Encapsula os componentes necessário para efetivar uma troca de informações entre do servidor para o cliente.

 public
    function Response: TRESTResponse;
    function Request: TRESTRequest;
    function Auth2: TOAuth2Authenticator;
    property AccessToken: string read GetAccessToken write SetAccessToken;
    procedure Clear; virtual;
    constructor create(ow: TComponent); override;
    destructor destroy; override;
    function Get(url: string; AResource: string = ''): string; virtual;
    function GetStream(AUrl: string; AResource: string; AStream: TStream)
      : integer; virtual;
    function SendStream(AUrl, AResource: string; AStream: TStream)
      : integer; virtual;
    function Post(url: string; AResource: string = ''): string; virtual;


 
 var rsp:string;
  with TRESTSocialClient.create(nil) do
  try
    rsp := Get('http://meuservidor/xxxx', '/GetCliente?codigo=1');  // chama o servidor                                                                 para pegar GetCliente.... finally
     free;
  end;

TRESTSocialClientDataset


Herança de TRESTSocialClient que associa a resposta do servidor um Dataset.

  with TRESTSocialClientDataset.create(nil) do
  try
    // pode indica um DATASET, para obter o retorno
    Dataset := MeuDataset;   // se nao for informado retorna um Dataset   TFDMemTable
    rootElement := 'result';  // espera um Array com as linhas da tabela;  
    rsp := Get('http://meuservidor/xxxx', '/GetCliente?codigo=1');  
                                                         // chama o servidor                                                                para pegar GetCliente....  
  finally
     free;
  end;

Exemplo:  REST.Social