Share Button

Impressionante o número de vezes que precisamos copiar os valores de atributos de um objeto para outro ou simplesmente criar um novo com as mesmas propriedades.
Conhecendo a classe TMVCBrPrototype notamos o poder de RTTI combinado com Generics nos permite facilitar o trabalho sem precisar digitar todos aquelas propriedades envolvendo os dois objetos.

base
Usando Generics para passar o tipo de classe a ser manipulado e com RTTI descobrimos quais os atributos desta classe… agora é só os valores de uma para o outro.

elementar meu caro
Uso de Generics tem por finalidade facilitar o RTTI pegar quais os atributos a serem copiados do objeto alvo. Para que a cópia ocorra, o objeto de origem e de destino precisam possuir o mesmo nome para o atributo. O ideal é que ambos possuam a mesma tipagem, ainda que não seja obrigatório.

  TMVCBrPrototype = Class(TInterfacedObject, IMVCBrPrototype)
  public
    class procedure Copy<T: Class>(ASource: T; ATarget: T); static;
    class function Clone<T: Class>(ASource: T): T; static;
    class function New<T: Class>: T; static;
  end;



// Como copiar de B para A

var B,A : TMinhaClasse;

...
    se:   B := TMinhaClasse.create;
          B.nome := 'x';

    clone:
         A := TMVCBrPrototype.Clone<TMinhaClasse>(B);   /// Cria A e copia B para A


    para copiar (requer que A e B já estejam criados):
        TMVCBrPrototype.Copy<TMinhaClasse>(B,A);   /// copia B para A

TMVCBrPrototype

Share Button

Trabalhar com RECORD é mais fácil administrar memória quando comparado com classes.

Como cada variável RECORD ocupa um endereço diferente com os dados e controla a sua retirada da memória com mais facilidade, prefiro usar RECORD para fazer CACHE de dados.

Por outro lado, temos vários acessos ao banco de dados que possuem parâmetros que estão nestes RECORD (alguém lembrará um CRUD). Uma solução é utilizar RTTI para ler os valores dos parâmetros que estão armazenados no RECORD e passar os valores para os parâmetros (TParams).

function TALQuery.FillParams<T>(rec: T): TALQuery;
var
  LList: TJsonValuesList;  // unit System.uJson
  LPair:TJsonPair;
  i:integer;
  prm:TParamBase;
begin
  result := self;
  LList := TJsonValuesList.Create();    // unit System.uJson
  try
    TJsonValue.GetRecordList<T>(LList, rec); // carrega os valores do RECORD em um LIST
    for I := 0 to params.count-1 do
         begin
            prm:= params[i];
            LPair := LList.names[ prm.Name ];
            if assigned(LPair) then
              case prm.DataType of
                 ftSmallint,ftInteger:
                   prm.AsInteger := LPair.JsonValue.GetValue<integer>;
                 ftFloat:
                   prm.AsFloat := LPair.JsonValue.GetValue<double>;
                 ftCurrency:
                   prm.AsCurrency := LPair.JsonValue.GetValue<Currency>;
                 ftDateTime,ftDate,ftTime :
                   prm.asDateTime := LPair.JsonValue.getValue<TDateTime>;
               else
                 prm.Value := LPair.JsonValue.getValue<string>;
              end;
         end;
  finally
    LList.free;
  end;
end;


/* aplicando */


Type
    TPedidoRecord = record
      ...
      pedido:integer;
      filial:integer;
      data:TDatetime;
    end;


var qry:   TMinhaQuery;
    rec : TPedidoRecord;
begin
    ....
    qry.fillParams<TPedidoRecord>(rec);
    ...
end;

 

Dependência: System.uJson

 

Share Button

Precisa fazer persistência local de configurações ?
Então um dia você ainda vai usar um arquivo JSON ao invés de um INI.

Ver uma classe para TJsonFile     Exemplo

  • Usando RTTI para escrever no TJsonFile

Ver o exemplo com fazer persistência de objeto usando RTTI para descobrir as propriedades a guardar no TJsonFile.WriteObject(…). Do outro lado TJsonFile.ReadObject(…) lê as propriedades no JSONFile e popula o objeto.

O funcionamento do RTTI é o mesmo descrito no post anterior

 

Share Button

É comum encontrar sistemas que utilizam arquivos TIniFiles para persistir informações locais.
O uso de TIniFiles impõe escrever muitas linhas para gravação e leitura do conteúdo.
No exemplo mostro como utilizar RTTI para gravar as propriedades de um objeto diretamente no arquivo INI / carregando as informações do arquivo INI para o objeto.

  • Para o exemplo considerar a seguinte classe base para gravação no arquivo INI:
// Classe a ser gravar no INI
  TIniSecaoClass = class
  private
    Fbase_datetime: TDatetime;
    Fbase_numerico: Double;
    Fbase_string: string;
    Fbase_integer: integer;
    Fbase_boolean: Boolean;
    procedure Setbase_datetime(const Value: TDatetime);
    procedure Setbase_numerico(const Value: Double);
    procedure Setbase_string(const Value: string);
    procedure Setbase_integer(const Value: integer);
    procedure Setbase_boolean(const Value: Boolean);
  public
    // propriedades a serem gravadas ou lidas no INI
    property base_string: string read Fbase_string write Setbase_string;
    property base_datetime: TDatetime read Fbase_datetime
      write Setbase_datetime;
    property base_numerico: Double read Fbase_numerico write Setbase_numerico;
    property base_integer: integer read Fbase_integer write Setbase_integer;
    property base_boolean: Boolean read Fbase_boolean write Setbase_boolean;
  end;
  • HELPERs para adicionar funcionalidade aos objetos existentes no DELPHI.

uses IniFiles, System.DateUtils, System.Rtti, System.TypInfo;

type

  {
    Fragmento de:    System.Classes.Helper
    https://github.com/amarildolacerda/helpers/blob/master/System.Classes.Helper.pas
  }
  TMemberVisibilitySet = set of TMemberVisibility;

  // RTTI para pegar propriedades do object
  TObjectHelper = class helper for TObject
  private
    procedure GetPropertiesItems(AList: TStrings;
      const AVisibility: TMemberVisibilitySet);
  end;

  // Adiciona Uso de RTTI para o INI
  TCustomIniFileHelper = class Helper for TCustomIniFile
  private
    procedure WriteObject(const ASection: string; AObj: TObject);
    procedure ReadObject(const ASection: string; AObj: TObject);
  public
  end;

  // Adiciona funções ao TValue
  TValueHelper = record helper for TValue
  private
    function IsNumeric: Boolean;
    function IsFloat: Boolean;
    function AsFloat: Extended;
    function IsBoolean: Boolean;
    function IsDate: Boolean;
    function IsDateTime: Boolean;
    function IsDouble: Boolean;
    function AsDouble: Double;
    function IsInteger: Boolean;
  end;


  • Métodos para gravação e leitura para o arquivo INI utilizando RTTI:
procedure TCustomIniFileHelper.WriteObject(const ASection: string;
  AObj: TObject);
var
  aCtx: TRttiContext;
  AFld: TRttiProperty;
  AValue: TValue;
begin
  aCtx := TRttiContext.Create;
  try
    for AFld in aCtx.GetType(AObj.ClassType).GetProperties do
    begin
      if AFld.Visibility in [mvPublic] then
      begin
        AValue := AFld.GetValue(AObj);
        if AValue.IsDate or AValue.IsDateTime then
          WriteString(ASection, AFld.Name, ISODateTimeToString(AValue.AsDouble))
        else if AValue.IsBoolean then
          WriteBool(ASection, AFld.Name, AValue.AsBoolean)
        else if AValue.IsInteger then
          WriteInteger(ASection, AFld.Name, AValue.AsInteger)
        else if AValue.IsFloat or AValue.IsNumeric then
          WriteFloat(ASection, AFld.Name, AValue.AsFloat)
        else
          WriteString(ASection, AFld.Name, AValue.ToString);
      end;
    end;
  finally
    aCtx.free;
  end;
end;

procedure TCustomIniFileHelper.ReadObject(const ASection: string;
  AObj: TObject);
var
  aCtx: TRttiContext;
  AFld: TRttiProperty;
  AValue, ABase: TValue;
begin
  aCtx := TRttiContext.Create;
  try
    for AFld in aCtx.GetType(AObj.ClassType).GetProperties do
    begin
      if AFld.Visibility in [mvPublic] then
      begin
        ABase := AFld.GetValue(AObj);
        AValue := AFld.GetValue(AObj);
        if ABase.IsDate or ABase.IsDateTime then
          AValue := ISOStrToDateTime(ReadString(ASection, AFld.Name,
            ISODateTimeToString(ABase.AsDouble)))
        else if ABase.IsBoolean then
          AValue := ReadBool(ASection, AFld.Name, ABase.AsBoolean)
        else if ABase.IsInteger then
          AValue := ReadInteger(ASection, AFld.Name, ABase.AsInteger)
        else if ABase.IsFloat or ABase.IsNumeric then
          AValue := ReadFloat(ASection, AFld.Name, ABase.AsFloat)
        else
          AValue := ReadString(ASection, AFld.Name, ABase.asString);
        AFld.SetValue(AObj, AValue);
      end;
    end;
  finally
    aCtx.free;
  end;
end;
  • Gravando o objeto no arquivo INI:

procedure TForm6.Button2Click(Sender: TObject);
begin
  // grava o objeto  OBJ no INI
  // inicializar OBJ antes de executar....
  with TIniFile.Create('teste.ini') do
    try
      WriteObject('SecaoClass', obj);
    finally
      free;
    end;

end;
  • Carregando o objeto com os dados do INI:
procedure TForm6.Button4Click(Sender: TObject);
begin
  // Ler os dados do INI para o OBJ
  with TIniFile.Create('teste.ini') do
    try
      ReadObject('SecaoClass', obj);
    finally
      free;
    end;
end;

Código Fonte no GIT

 

Para escrever arquivos JSON com as configurações ver o post seguinte

 

Share Button

Por algum tempo não dei muita atenção para a RTTI. Tudo era muito trabalhoso. Quando cheguei na família XE notei que as coisa tinham mudado bastante, então passei a fazer uso de umas coisas aqui.. outras ali… quando nem tinha me dado conta as coisas estavam ficando sérias.

RTTI é uma ferramenta poderosa, mas dá trabalho. Gostaria de simplificar um pouco as coisa para poder usar com mais frequência e com mais segurança.

Depois de várias tentativas concluí que o caminha mais rápido seria usar Class Helper para entregar ao TObject suporte mais facilitado para as chamadas RTTI.

 

  TObjectHelper = class helper for TObject
    ....
    // RTTI
    property Properties[AName: string]: TValue read GetProperties
      write SetProperties;
    property Fields[AName: string]: TValue read GetFields write SetFields;
    property Methods[AName: String]: TRttiMethod read GetMethods;
    function HasAttribute(aMethod: TRttiMethod;
      attribClass: TCustomAttributeClass): Boolean;
    function InvokeAttribute(attribClass: TCustomAttributeClass;
      params: array of TValue): Boolean;
    function InvokeMethod(AName: string; params: array of TValue): Boolean;

  end;

Ver classe completa: RTTI Class Helper
* alguns métodos foram alterados para resolver conflitos.
 

Exemplo:

 

{$R *.dfm}
uses System.Classes.helper, System.TypInfo;

procedure TForm3.Button1Click(Sender: TObject);
begin
   Button1.GetPropertiesList( ListBox1.Items );   // pega uma lista de properiedades do Button1
   edit2.Text := Button1.Properties['Caption'].AsString;   // pega o valor da propriedade caption
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
   button1.Properties[ 'Caption' ] := edit2.Text;  // altera a proprieda do Caption
end;

procedure TForm3.Button3Click(Sender: TObject);
begin
  button1.GetFieldsList( ListBox2.Items, [mvPrivate,mvPublic]  );
end;


Ver Exemplos

 

.

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.