Muitos programadores tem tido problema com o envio de informações do tipo ponto flutuante e datetime de um servidor datasnap para uma aplicação cliente, onde ao deserializar a informação (unmarshall) a mesma causa um erro no Delphi.

A solução que encontrei é bem simples e envolve um truque de orientação à objeto para funcionar.

Digamos que você queira enviar a seguinte classe de um servidor datasnap para uma aplicação cliente, utilizando o JSON:

TCliente = class
  private
    FID: Integer;
    FNome: String;
    FAniversario: TDateTime;
    FMensalidade: Double;

  public
    property ID: Integer read FID write FID;
    property Nome: String read FNome write FNome;
    property Aniversario: TDateTime read FAniversario write FAniversario;
    property Mensalidade: Double read FMensalidade write FMensalidade;
end;

Você teria então o seguinte método em um ServerMethod remoto:

function TmyServerMethod.GetCliente: TCliente;
begin
  Result := TCliente.Create;
  Result.ID := 1;
  Result.Nome:= 'Cliente de Teste';
  Result.Aniversario:= StrToDateTime('04/07/1972 18:00:00');
  Result.Mensalidade:= 120.50;
end;

Ao consumir este método na aplicação cliente, a classe será serializada no formato JSON e enviada do servidor para o cliente. Quando o cliente tenta deserializar a classe ocorre o seguinte erro:


"Project XXXXXX.exe raised exception class TDBXError with message 'JSON byte stream cannot be parserd correctly into a JSON value'.

Porque isso ocorre? Existe um bug no datasnap, que permanece na versão XE, em relação a configurações regionais de idioma no processo de transformação de um objeto em formato JSON e na transformação novamente para objeto. No processo de transformação de JSON para Objeto (Unmarshall) ocorre este erro com os campos do tipo Double e TDatetime (que internamente são armazenados como double).

Este bug pode vir a ser corrigido em atualizações futuras do Delphi, mas enquanto isso não ocorre a solução é bem simples: basta fazermos uma pequena mudança em nossa classe TCliente, conforme a baixo:

TCliente = class
  private
    FID: Integer;
    FNome: String;
    FAniversario: String;
    FMensalidade: String;

    function GetAniversario: TDateTime;
    procedure SetAniversario(value : TDateTime);
    function GetMensalidade: Double;
    procedure SetMensalidade(value : Double);

  public
    property ID: Integer read FID write FID;
    property Nome: String read FNome write FNome;
    property Aniversario: TDateTime read GetAniversario write SetAniversario;
    property Mensalidade: Double read GetMensalidade write SetMensalidade;
end;

A implementação dos novos métodos seria da seguinte forma:


function TCliente.GetAniversario: TDateTime;
begin
  if(FAniversario <> EmptyStr) then                     // Você pode fazer uma validação mais detalhada aqui
    Result := StrToDateTime(FAniversario)
  else
    Result := 0;
end;

procedure TCliente.SetAniversario(value: TDateTime);
begin
  FAniversario := FormatDateTime('DD/MM/YYYY HH:NN:SS', value);
end;

function TCliente.GetMensalidade: Double;
begin
  Result := FloatToStrDef(FMensalidade,0);
end;

procedure TCliente.SetMensalidade(value: Double);
begin
  FMensalidade := FloatToStr(value);
end;


Desta forma, os campos Aniversário e Mensalidade serão armazenados internamente na classe no formato String, mas externamente serão manipulados pelos seus métodos Get/Set no seu formato original, de forma transparente para a aplicação.

Quando o método GetCliente for consumido na aplicação cliente, o servidor enviará a classe TCliente no formato JSON, ficando a sua representação da seguinte forma:

{"type":"uCliente.TCliente","id":1,"fields":{"FID":1,"FNome":"Cliente de Teste", "FAniversario: "04/07/1972 18:00:00", "FMensalidade":"120,50"}}

Note que somente o conteúdo das variáveis e representado na notação JSON e não as propriedades, e como as variáveis FAniversario e FMensalidade foram declaradas como String o seu conteúdo segue no mesmo formato. Ao deserializar o JSON para o formato TCliente nenhum erro ocorre porque não é feita a conversão de tipo destes campos, a conversão será feita posteriormente ao ser acessada as propriedades Aniversário e Mensalidade da classe TCliente.

Tenho conseguido trabalhar com sucesso desta forma, e espero que vocês possam conseguir também.