Share Button

Construindo o Servidor RESTServer com exemplos… retornar um TDataset em um JSONObject X “Reflection”.

Para iniciar utilizei o modelo criado pelo delphi: new -> Datasnap REST Application. Criado o projeto alterei a unit abaixo:

GIT com fontes para as dependências


unit ServerMethodsUnit1;

interface

uses System.SysUtils, System.Classes, System.Json,
  Data.FireDACJSONReflect,
  Datasnap.DSServer, Datasnap.DSAuth, Datasnap.DSProviderDataModuleAdapter,
  FireDAC.Stan.StorageJSON, FireDAC.Stan.StorageBin;

type
{$METHODINFO ON}
  TServerMethods1 = class(TDataModule)
    FDStanStorageJSONLink1: TFDStanStorageJSONLink;
    FDStanStorageBinLink1: TFDStanStorageBinLink;
  private
    { Private declarations }
  public
    { Public declarations }
    // original criado pelo DELPHI
    function EchoString(Value: string): string;
    function ReverseString(Value: string): string;

    // passa o codigo do cliente e retorna os dados do cliente
    function GetCliente(codigo: int64): TJSONValue;  // exemplo retornando um JSONObject
    function GetCliente2(codigo: int64): TFDJSONDataSets;  // usando Reflection

    // retorna na mesma resposta CLIENTE + ITENS
    function GetNotaFiscal(ANumero: integer): TFDJSONDataSets;

  end;
{$METHODINFO OFF}

implementation

{$R *.dfm}

uses System.StrUtils, FireDAC.ObjectDataSet, Data.db.helper;

function TServerMethods1.EchoString(Value: string): string;
begin
  Result := Value;
end;

function TServerMethods1.ReverseString(Value: string): string;
begin
  Result := System.StrUtils.ReverseString(Value);
end;

Criando as classes a serem carregadas no TDataset usando RTTI:

type
  TClientes = class
  private
    FCodigo: int64;
    FNome: string;
    FCidade: string;
    FDebitos: double;
    FEndereco: string;
    FEstado: String;
    procedure SetCidade(const Value: string);
    procedure SetCodigo(const Value: int64);
    procedure SetDebitos(const Value: double);
    procedure SetEndereco(const Value: string);
    procedure SetEstado(const Value: String);
    procedure SetNome(const Value: string);
  public
    property codigo: int64 read FCodigo write SetCodigo;
    property Nome: string read FNome write SetNome;
    property Cidade: string read FCidade write SetCidade;
    property Estado: String read FEstado write SetEstado;
    property Endereco: string read FEndereco write SetEndereco;
    property Debitos: double read FDebitos write SetDebitos;
  end;

  TNotaFiscalItens = class
  private
    FPreco: double;
    FCodigo: string;
    FQtde: double;
    FNome: string;
    procedure SetCodigo(const Value: string);
    procedure SetNome(const Value: string);
    procedure SetPreco(const Value: double);
    procedure SetQtde(const Value: double);
  public
    property codigo: string read FCodigo write SetCodigo;
    property Nome: string read FNome write SetNome;
    property Qtde: double read FQtde write SetQtde;
    property Preco: double read FPreco write SetPreco;
  end;

Quando o cliente fizer chamada para GetCliente irá passar o código do cliente, internamente o servidor irá procurar no banco de dados e retornar um JSONObject com os dados do cliente. Este processo permite retornar uma informação mais genérica, importante quando os clientes que irão consumir tenham múltiplas linguagens ou múltiplos desenvolvedores que nem sempre serão Delphianos.


function TServerMethods1.GetCliente(codigo: int64): TJSONValue;
// TFDJSONDataSets;
var
  ds: TObjectDataSet;
  cli: TClientes;
begin
  // buscar os dados no banco de dados com codigo passado pelo cliente...

  // resposta para o cliente;

  // meus dados no firedac
  // usei um ObjectDataset somento para não precisar criar uma conexão e um query
  ds := TObjectDataSet.Create(self, TClientes);  // usa RTTI para mapear coluna do TDataset
  try
    ds.Open;
    ds.append;
    with ds do
    begin
      FieldByName('codigo').Value := 1;
      FieldByName('nome').Value := 'Embarcadero SA';
      FieldByName('Endereco').Value := 'Rua...xxxx...,10';
      FieldByName('Cidade').Value := 'Sao Paulo';
      FieldByName('Estado').Value := 'SP';
      FieldByName('Debitos').Value := 100000.12;
    end;
    ds.Post;

    // retorna um JsonObject
    Result := TJSONObject.ParseJSONValue(ds.ToJson);
  finally
    ds.Free;
  end;
end;

Se os clientes que irão consumir o serviço forem sempre RestClient de Delphi, então retornar um objeto mais preparado para a estrutura do RestClient pode ser um opção. Retornando um objetos TFDJSONDataSets aumenta os dados a serem retornados ao cliente mas pode facilitar a implementação para os cliente Delphi.

// retorna um objeto FireDAC JSON  Reflection
// isto estabelece uma boa integração entre aplicativos que usam FireDAC nos clientes.
// se o cliente não for FireDAC... talves seja interessante pensar em retornar um
//   formato mais generico

function TServerMethods1.GetCliente2(codigo: int64): TFDJSONDataSets;
var
  ds: TObjectDataSet;
  cli: TClientes;
begin
  // buscar os dados no banco de dados com codigo passado pelo cliente...

  // resposta para o cliente;
  Result := TFDJSONDataSets.Create;
  // meus dados no firedac
  // usei um ObjectDataset somento para não precisar criar uma conexão e um query
  ds := TObjectDataSet.Create(nil, TClientes);
  try
    ds.Open;
    ds.append;
    with ds do
    begin
      FieldByName('codigo').Value := 1;
      FieldByName('nome').Value := 'Embarcadero SA';
      FieldByName('Endereco').Value := 'Rua...xxxx...,10';
      FieldByName('Cidade').Value := 'Sao Paulo';
      FieldByName('Estado').Value := 'SP';
      FieldByName('Debitos').Value := 100000.12;
    end;
    ds.Post;

    TFDJSONDataSetsWriter.ListAdd(Result, 'CLIENTE', ds);
  finally
    // ds.Free;   -- eh destruido pelo Writer
  end;
end;

Para retornar mais de uma estrutura de informações no mesma chamada, pode-se utilizar TFDJSONDataSets que permite como no exemplo retornar os dados do Cliente e também os dados dos itens da nota fiscal (no exemplo) no retorno da mesma chamada.


// retornando mais de uma tabala na mesma consulta
// retorna  CLIENTE  e ITENS da nota fiscal
function TServerMethods1.GetNotaFiscal(ANumero: integer): TFDJSONDataSets;
var
  ds: TObjectDataSet;
  cli: TClientes;
  dsItens: TObjectDataSet;
  itens: TNotaFiscalItens;
begin
  // buscar os dados no banco de dados com codigo passado pelo cliente...

  // resposta para o cliente;
  Result := TFDJSONDataSets.Create;
  // meus dados no firedac
  // usei um ObjectDataset somento para não precisar criar uma conexão e um query
  ds := TObjectDataSet.Create(nil, TClientes);
  try
    ds.Open;
    ds.append;
    with ds do
    begin
      FieldByName('codigo').Value := 1;
      FieldByName('nome').Value := 'Embarcadero SA';
      FieldByName('Endereco').Value := 'Rua...xxxx...,10';
      FieldByName('Cidade').Value := 'Sao Paulo';
      FieldByName('Estado').Value := 'SP';
      FieldByName('Debitos').Value := 100000.12;
    end;
    ds.Post;

    TFDJSONDataSetsWriter.ListAdd(Result, 'CLIENTE', ds);

    // usa RTTI para mapear o objeto para TFDMemTable
    dsItens := TObjectDataSet.Create(nil, TNotaFiscalItens);
    dsItens.Open;
    itens := TNotaFiscalItens.Create;
    with itens do
    begin
      codigo := '000001';
      Nome := 'Margarina';
      Qtde := 2;
      Preco := 8.5;
    end;
    dsItens.ObjectList.Add(itens);  // adiciona o objeto ao ObjectDataSet

    with itens do
    begin
      codigo := '000002';
      Nome := 'Pao Frances';
      Qtde := 1;
      Preco := 15;
    end;
    dsItens.ObjectList.Add(itens);

    TFDJSONDataSetsWriter.ListAdd(Result, 'ITENS', dsItens);

  finally
    // ds.Free;   -- eh destruido pelo Writer
  end;
end;

.....
end.

Share Button

Clone de ObjectDataset-JEDI para FireDAC….

Encontra-se no GIT o código para carregar um TObject ou TClass diretamente em um Dataset.
A implementação faz uso de RTTI para ler as propriedades do objeto e mapeia os tipo para as colunas do TFDMemTable do FireDAC.

Exemplo de uso:


// Exemplo de uma classe para mapear no Dataset
  TMinhaClasse = class
  private
    ....
  published
    property Nome: string read FNome write SetNome;
    property id: integer read Fid write Setid;
    property valor: double read Fvalor write Setvalor;
    property Data: TDatetime read Fdata write Setdata;
    property Dinheiro: Currency read FDinheiro write SetDinheiro;
    property Cliente: double read FCliente write SetCliente;
  end;

.....
uses FireDac.ObjectDataSet;
.....
// iniciando o Dataset com o link da Classe...
var ods:TObjectDataset;
begin
  ods := TObjectDataset.Create(self, TMinhaClasse);
  //DataSource1.DataSet := ods; // associa o Dataset a um Datasource
  ods.Open;
end;

Código Fontes no GIT

Lidando com uma lista externa

A classe mantem uma lista interna com os objetos publicando pela propriedade  TObjectDataset.ObjectList,  se você já possui uma lista externa pode utilizar os métodos:

procedure LoadFromList( AList:TList );   // carrega a lista externa para o TDataset
procedure SaveToList( AList:TList );        //  preeche a lista externa com os dados do TDataset;

Emprestando o exemplo do amigo (Marlon)

   TCliente;
   var
     cli : TCliente;
     ods : TObjectDataSet;
   begin
     cli := TCliente.Create;
     cli.ID := 1;
     cli.Nome := ''Marlon;
     cli.Salario := 123.45;
     cli.Dt_Nasc := Date;
     ods := TObjectDataSet.Create(Self, TCliente);
     ods.Open;
     DataSource1.DataSet := ods;

     // pode acrescentar o registro no Dataset;
     ods.append;
     ods.ObjectToField(cli);   // não precisa fazer free... a lista interna faz a liberação de memoria.
     ods.post;

     // outra opção seria:
     ods.append;
     ods.FieldByname('id').asInteger := 1;
     ods.FieldByName('nome').asString := 'Marlon';
     ...
     ods.post;

 

Contribuição do  Marlon


unit Unit56;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDac.ObjectDataSet,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
  FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
  Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Mask, Vcl.DBCtrls,
  Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Grids, Vcl.DBGrids;

type

  TCliente = class
  private
    FDt_Nasc: TDate;
    FSalario: Currency;
    FID: Integer;
    FNome: String;
    procedure SetDt_Nasc(const Value: TDate);
    procedure SetID(const Value: Integer);
    procedure SetNome(const Value: String);
    procedure SetSalario(const Value: Currency);
  public
    property ID : Integer read FID write SetID;
    property Nome : String read FNome write SetNome;
    property Salario : Currency read FSalario write SetSalario;
    property Dt_Nasc : TDate read FDt_Nasc write SetDt_Nasc;
  end;


  TForm56 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    DBEdit1: TDBEdit;
    DBEdit2: TDBEdit;
    DBEdit3: TDBEdit;
    DBEdit4: TDBEdit;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    ods : TObjectDataSet;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form56: TForm56;

implementation

{$R *.dfm}

{ TCliente }

procedure TCliente.SetDt_Nasc(const Value: TDate);
begin
  FDt_Nasc := Value;
end;

procedure TCliente.SetID(const Value: Integer);
begin
  FID := Value;
end;

procedure TCliente.SetNome(const Value: String);
begin
  FNome := Value;
end;

procedure TCliente.SetSalario(const Value: Currency);
begin
  FSalario := Value;
end;

procedure TForm56.Button1Click(Sender: TObject);
var
  cli : TCliente;
begin
  cli := TCliente.Create;

  ods.FieldToObject(cli);

  Edit1.Text := IntToStr(cli.ID);
  Edit2.Text := cli.Nome;
  Edit3.Text := CurrToStr(cli.Salario);
  Edit4.Text := DateToStr(cli.Dt_Nasc);

end;

procedure TForm56.Button2Click(Sender: TObject);
var
  cli : TCliente;
begin
  cli := TCliente.Create;

  cli.ID := StrToIntDef(Edit1.Text, 0);
  cli.Nome := Edit2.Text;
  cli.Salario := StrToCurrDef(Edit3.Text,0);
  cli.Dt_Nasc := StrToDateDef(Edit4.Text, Date);

  ods.Insert;
  ods.ObjectToField(cli);
  ods.Post;

end;

procedure TForm56.FormCreate(Sender: TObject);
begin
  ods := TObjectDataSet.Create(Self, TCliente);
  ods.Open;
  DataSource1.DataSet := ods;
end;

end.

 

Share Button

As voltas com código legado…

Encontra-se no GIT o código para carregar um TObject ou TClass diretamente em um Dataset.
A implementação faz uso de RTTI para ler as propriedades do objeto e mapeia os tipo para as colunas do TJvMemoryData do JEDI (depende de ter instalado o JEDI-JvMemoryDataSet).

Clone para FireDAC

Exemplo de uso:


// Exemplo de uma classe para mapear no Dataset
  TMinhaClasse = class
  private
    ....
  published
    property Nome: string read FNome write SetNome;
    property id: integer read Fid write Setid;
    property valor: double read Fvalor write Setvalor;
    property Data: TDatetime read Fdata write Setdata;
    property Dinheiro: Currency read FDinheiro write SetDinheiro;
    property Cliente: double read FCliente write SetCliente;
  end;

.....
uses Data.ObjectDataSet;
.....
// iniciando o Dataset com o link da Classe...
var ods:TObjectDataset;
begin
  ods := TObjectDataset.Create(self, TMinhaClasse);
  //DataSource1.DataSet := ods; // associa o Dataset a um Datasource
  ods.Open;
end;



Código Fontes no GIT