Delphi Rest.JSON do not work on simple class - json

I am working on my main project and I have to "JSON serialize" a simple class :
TServer = class
public
Host: string;
end;
TServerList = class
public
Servers: TArray<TServer>;
end;
procedure Test;
begin
var SrvLst := TServerList.Create;
SetLength(SrvLst.Servers, 1);
SrvLst.Servers[0] := TServer.Create;
SrvLst.Servers[0].Host := 'TEST';
var Json := TJson.ObjectToJsonString(SrvLst);
// >> Json = '{"servers":[{"host":"TEST"}]}' , it works...
var SrvLst2 := TJson.JsonToObject<TServerList>(Json);
// >> SrvLst2.Server is empty ?? what's wrong ??
// memory leaks.. nevermind
end;
I can't see what is wrong... Any idea ?
Thank you.

You need to either declare the classes this way:
TServer = class
private
FHost: string;
public
property Host: string read FHost write FHost;
end;
TServerList = class
private
FServers: TArray<TServer>;
public
property Servers: TArray<TServer> read FServers write FServers;
end;
or this way:
type
TServer = class
public
[JSONName('Host')]
Host: string;
end;
TServerList = class
public
[JSONName('Servers')]
Servers: TArray<TServer>;
end;
Note that the latter requires to use REST.Json.Types.

Related

the address of Self in constructor in freepascal?

Below is the output of test code.
Why are the addresss of instance(#a) and the address of Self(#Self) in constructor different?
I think both should be the same.
The addresss of class instance and the address of this in constructor are same in c++.
Is it possible to get the address equal to the addresss of instance(#a) in constructor?
Lazarus 2.2.0 64bit for windows.
#Self=00000000013FFDC8
#a=00000000013FFE38
a.i=3
a_ptr^.i=3
ga_ptr^.i=5872 << invalid value
unit unitmain;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils;
type
ClassA = class
public
constructor Create(i : Integer);
var
i : Integer;
end;
PClassA = ^ClassA;
var
ga_ptr : PClassA;
procedure TestSelfAddr();
implementation
constructor ClassA.Create(i : Integer);
begin
Self.i := i;
ga_ptr := #Self;
Writeln(Format('#Self=%P', [#Self]));
end;
procedure TestSelfAddr();
var
a : ClassA = nil;
a_ptr : PClassA = nil;
begin
a := ClassA.Create(3);
a_ptr := #a;
Writeln(Format('#a=%P', [#a]));
Writeln(Format('a.i=%d', [a.i]));
Writeln(Format('a_ptr^.i=%d', [a_ptr^.i]));
Writeln(Format('ga_ptr^.i=%d', [ga_ptr^.i])); // << invalid value
end;
end.

Pascal Debian Jessie TryGetData not working

when I Try in Pascal under Debian Jessie
TStringObjectMap2 = class
private
type
TStrObjMap = class(specialize TFPGMap<string, THostHandler>);
private
FObjects: TStrObjMap;
FDefaultObject: THostHandler;
FFreeObjects: boolean;
function GetObject(const AKey: string): THostHandler;
procedure SetDefault(AValue: THostHandler);
procedure SetObject(const AKey: string; AValue: THostHandler);
public
constructor Create(FreeObjects: boolean);
destructor Destroy; override;
procedure RemoveHandler(const AKey: string);
function TryGetObject(const AKey: string; out AObject: THostHandler): boolean;
property DefaultObject: THostHandler read FDefaultObject write SetDefault;
property Objects[const AKey: string]: THostHandler read GetObject write SetObject; default;
end;
the Line
Result := FObjects.TryGetData(AKey, AObject);
can not compiled
But under Win 10 there is no problem.
Whats going wrong?

LockBox 3 - PKCS padding does not seem to be supported

I have a class hierarchy which abstracts the Lockbox crypto components.
Specifically, we are interested in AES-265 with PKCS#5 padding.
The class instance is set correctly with CBC and the encoding explicitly set to ANSI but the resulting output is scrambled, which means that the padding is off.
Alas, CBC supports more than one padding scheme and I think Lockbox isn't using the one we need.
Do you have any ideas where I am going wrong?
This is the class hierarchy's relevant code:
TsmEncryptBase = class(TInterfacedObject, IsmEncryption)
private
FLib: TCryptographicLibrary;
protected
FCodec: TCodec;
function Encrypt: Boolean; virtual; abstract;
function Decrypt: Boolean; virtual; abstract;
public
constructor Create(const APassword: string;
const aCipherId: string = 'native.AES-256';
const aChainModeId:string = 'native.CBC');
destructor Destroy; override;
end;
constructor TsmEncryptBase.Create(const APassword: string; const aCipherId:
string; const aChainModeId: string);
begin
inherited Create;
FLib := TCryptographicLibrary.Create(nil);
// FLib.RegisterBlockChainingModel( TPure_ECB.Create as IBlockChainingModel);
FCodec := TCodec.Create(nil);
FCodec.CryptoLibrary := FLib;
FCodec.StreamCipherId := uTPLb_Constants.BlockCipher_ProgId;
FCodec.BlockCipherId := aCipherId;
FCodec.ChainModeId := uTPLb_Constants.CBC_ProgId;
FCodec.Password := APassword;
end;
TsmFileEncryptAES = class(TsmEncryptBase)
private
FPlainTextFileName: string;
FEncryptedFileName: string;
protected
function Encrypt: boolean; override;
function Decrypt: Boolean; override;
public
constructor Create(const APlainTextFileName, AEncryptedFileName,
APassword: string);
end;
constructor TsmFileEncryptAES.Create(const APlainTextFileName, AEncryptedFileName,
APassword: string);
begin
inherited Create(APassword);
FPlainTextFileName := APlainTextFileName;
FEncryptedFileName := AEncryptedFileName;
FCodec.Encoding := TEncoding.ANSI;
end;
The code is used like so:
procedure TForm1.AESFileDecryptClick(Sender: TObject);
var lEncrypt: IsmEncryption;
begin
lEncrypt := TsmFileEncryptAES.Create(AESFileSaveTo.AsString,
AESSourceFile.AsString, AESFileKey.AsString);
lEncrypt.Decrypt;
end;
Any ideas?

Can I declare a datasnap server method with array parameters?

Parameters of Server methods in datasnap supports a number of data type. TArray<string> or array of string shall be basic type, but the following Test method is not working:
type{$M+}
TMyModule = class(TDSServerModule)
public
function ReverseString(Value: string): string;
function Test: TArray<string>;
end;
Is there an alternative way to get array works with server methods?
Yes, there is.
All you need to do is to encapsulate your array in an object.
Here is an example for an array of integers :
`
unit TransferObjects;
interface
type
TIntArray = TArray<Integer>;
TIntegerArray = class(TObject)
private
FValues: TIntArray;
function GetValues: TIntArray;
procedure SetValues(const Value: TIntArray);
public
property Values : TIntArray read GetValues write SetValues;
constructor Create;
function Length : Integer;
procedure Resize(Count : Integer);
end;
implementation
{ TIntegerArray }
constructor TIntegerArray.Create;
begin
inherited create;
Resize(0);
end;
function TIntegerArray.GetValues: TIntArray;
begin
Result := FValues;
end;
function TIntegerArray.Length: Integer;
begin
Result := System.Length(FValues);
end;
procedure TIntegerArray.Resize(Count : Integer);
begin
SetLength(FValues, Count);
end;
procedure TIntegerArray.SetValues(const Value: TIntArray);
begin
FValues := Value;
end;
end.
unit ServerMethodsUnit1;
interface
uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth,
TransferObjects;
type
{$METHODINFO ON}
TServerMethods1 = class(TComponent)
private
{ Private declarations }
public
{ Public declarations }
function ReverseArray(Value : TIntegerArray) : TIntegerArray;
end;
{$METHODINFO OFF}
implementation
function TServerMethods1.ReverseArray(Value: TIntegerArray): TIntegerArray;
var
i, l : Integer;
Erg : TIntegerArray;
begin
Erg := TIntegerArray.Create;
Erg.Resize(Value.Length);
for i := 0 to Value.Length-1 do
begin
l := Value.Length-1-i;
Erg.Values[l] := Value.Values[i];
end;
Result := Erg;
end;
end.
`
This may not be very elegant, but it works (tested with XE6).
Like this the object can be (un)marshaled internally with JSON, the same code using interfaces instead does not work.
hth

Return Object Null datasnap delphi XE2 and consuming in java Android

When I do not want to return any object and return nil on the server, the proxy classes generated an exception that could not convert on TJSONNull TJSONObject .. is this normal? what can I do to return null or tjsonnulll?
function TServerMethods.GetPais(Codigo: Integer; var Erro:String): TPais;
var
qry: TZQuery;
begin
Result := nil;
try
qry := CreateQueryConectado(Erro);
with qry do
begin
Close;
SQL.Clear;
SQL.Add(SQL_GET_LISTPAISES);
SQL.Add(' WHERE pais_codigo = :P00 ');
ParamByName('P00').AsInteger := codigo;
Open;
if not IsEmpty then
begin
Result := CreatePais;
Result.Codigo := FieldByName('pais_codigo').AsInteger;
Result.Nome := FieldByName('pais_descricao').AsString;
Result.Lingua := FieldByName('pais_lingua').AsString;
Result.Moeda := FieldByName('pais_moeda').AsString;
Result.TaxaCambio := FieldByName('pais_taxacambio').AsCurrency;
Result.Locale := FieldByName('pais_locale').AsString;
end;
end;
except
on e: Exception do
begin
Erro := TratarException(e);
end;
end;
end;
For example a query from a customer does not exist.
this a error in the proxy classes
java.lang.ClassCastException: com.embarcadero.javaandroid.TJSONNull cannot be cast to com.embarcadero.javaandroid.TJSONObject
08-15 11:55:03.759: W/System.err(28204): at com.embarcadero.javaandroid.DSProxy$TServerMethods.GetPais(DSProxy.java:1773)
is the proxy
private DSRESTParameterMetaData[] TServerMethods_GetPais_Metadata;
private DSRESTParameterMetaData[] get_TServerMethods_GetPais_Metadata() {
if (TServerMethods_GetPais_Metadata == null) {
TServerMethods_GetPais_Metadata = new DSRESTParameterMetaData[]{
new DSRESTParameterMetaData("Codigo", DSRESTParamDirection.Input, DBXDataTypes.Int32Type, "Integer"),
new DSRESTParameterMetaData("Erro", DSRESTParamDirection.InputOutput, DBXDataTypes.WideStringType, "string"),
new DSRESTParameterMetaData("", DSRESTParamDirection.ReturnValue, DBXDataTypes.JsonValueType, "TPais"),
};
}
return TServerMethods_GetPais_Metadata;
}
/**
* #param Codigo [in] - Type on server: Integer
* #param Erro [in/out] - Type on server: string
* #return result - Type on server: TPais
*/
public static class GetPaisReturns {
public String Erro;
public TJSONObject returnValue;
}
public GetPaisReturns GetPais(int Codigo, String Erro) throws DBXException {
DSRESTCommand cmd = getConnection().CreateCommand();
cmd.setRequestType(DSHTTPRequestType.GET);
cmd.setText("TServerMethods.GetPais");
cmd.prepare(get_TServerMethods_GetPais_Metadata());
cmd.getParameter(0).getValue().SetAsInt32(Codigo);
cmd.getParameter(1).getValue().SetAsString(Erro);
getConnection().execute(cmd);
GetPaisReturns ret = new GetPaisReturns();
ret.Erro = cmd.getParameter(1).getValue().GetAsString();
ret.returnValue = (TJSONObject)cmd.getParameter(2).getValue().GetAsJSONValue();
return ret;
}
Tanks!