You are not logged in.
Pages: 1
Hi
I utilise your RecordSaveJSON and RecordLoadJSON to serialize/deserialize my json between my ms windows client and server.
This works extremely well and very fast (obviously). Plus only one line...
However, I'm trying to create a mobile client (Android) now to speak to my remote server and trying to see if there's anything equivalent that I can use there.
I'm looking into the SynCrossPlatformJSON unit, but looking through the doco, that only works with TPersistent descendants? My DTO are records. Can you suggest anything? I'd like to keep the same DTO structure of records.
Currently, this is what I have working via Windows:
DTO:
type
TAccountDetailsDTORec = packed record
Id: Integer;
Description: string;
FNDepositAmount: Currency;
OrdId: Integer;
end;
TAccountDetailsDTORecArray = TArray<TAccountDetailsDTORec>;
and my common, generic methods for deserializing are:
class function TmORMotRecParser.JsonToDTO<T>(const JsonString: string): T;
begin
if not RecordLoadJson(Result, StringToUTF8(JsonString), TypeInfo(T)) then
raise EParserException.CreateFmt('Unable to parse DTO Json string: "%s"', [JsonString]);
end;
// for arrays
class function TmORMotRecParser.JsonToDTOArray<T>(const JsonString: string): T;
begin
DynArrayLoadJSON(Result, Pointer(StringToUTF8(JsonString)), TypeInfo(T));
end;
and serializing:
class function TmORMotRecParser.DTOToJson<T>(const DTO: T): string;
begin
Result := UTF8ToString(RecordSaveJSON(DTO, TypeInfo(T)));
end;
If I use [woStoreClassName] when serializing an object which contains a property/field of type TObjectList it correctly serializes to Json as expected using ObjectToJson.
Normally, when deserializing, you would pass in the expected class name (assuming no custom serializer registered) to tell JsonToObject what class to create the objects of.
However, with nested List objects, there's no opportunity to do that as JsonToObject will call (TPropInfo)P^.ClassFromJSON. This in turn will ultimately recurse back into JsonToObject but always passes nil as the class to construct the list objects for. Is there anyway to let it read the class name that is stored in the Json string or am I forced to use a custom serializer with these types of objects?
A simple example:
TMyObj2 = class(TPersistentWithCustomCreate)
private
FTesting: string;
public
constructor Create; override;
published
property Testing: string read FTesting write FTesting;
end;
TMyObject = class(TPersistentWithCustomCreate)
private
FAValue1: string;
FAValue2: string;
FAValue3: Integer;
FMyObj2: TMyObj2;
FNextLevel: TNextLevel;
FMyObj2List: TObjectList;
public
constructor Create; override;
destructor Destroy; override;
function SetCreate(AValue1, AValue2: string; AValue3: Integer): TMyObject;
published
property MyObj2: TMyObj2 read FMyObj2;
property NextLevel: TNextLevel read FNextLevel;
property AValue1: string read FAValue1 write FAValue1;
property AValue2: string read FAValue2 write FAValue2;
property AValue3: Integer read FAValue3 write FAValue3;
property MyObj2List: TObjectList read FMyObj2List;
end;
{ TMyObject }
constructor TMyObject.Create;
begin
FMyObj2 := TMyObj2.Create;
FNextLevel := TNextLevel.Create;
FMyObj2List := TObjectList.Create;
end;
function TMyObject.SetCreate(AValue1, AValue2: string; AValue3: Integer): TMyObject;
begin
FAValue1 := AValue1;
FAValue2 := AValue2;
FAValue3 := AValue3;
Result := Self;
end;
destructor TMyObject.Destroy;
begin
FMyObj2List.Free;
FNextLevel.Free;
FMyObj2.Free;
inherited;
end;
procedure TestmORMot_NestedObjectList;
var
List: TObjectList;
Json: RawUTF8;
Valid: Boolean;
MyObj2: TMyObj2;
begin
List := TObjectList.Create;
try
List.Add(TMyObject.Create.SetCreate('a','b', 1));
List.Add(TMyObject.Create.SetCreate('c','d', 2));
TMyObject(List[0]).MyObj2.Testing := 'Testing 1';
TMyObject(List[1]).MyObj2.Testing := 'Testing 2';
MyObj2 := TMyObj2.Create;
MyObj2.Testing := 'My Obj Item 1';
TMyObject(List[0]).MyObj2List.Add(MyObj2);
MyObj2 := TMyObj2.Create;
MyObj2.Testing := 'My Obj Item 2';
TMyObject(List[0]).MyObj2List.Add(MyObj2);
Json := ObjectToJson(List);
Writeln(Utf8ToString(Json));
finally
List.Free;
end;
Writeln('===================================================');
List := TObjectList.Create;
try
JsonToObject(List, @Json[1], Valid, TMyObject);
if valid then
begin
Writeln('Testing is - ' + TMyObject(List[1]).MyObj2.Testing);
Writeln(List.Count);
end
else
Writeln('Not Valid');
finally
List.Free;
end;
end;
This will result in this Json:
[{"ClassName":"TMyObject","MyObj2":{"ClassName":"TMyObj2","Testing":"Testing 1"},"NextLevel":{"ClassName":"TNextLevel","NextLevelDesc":"","TopLevelDesc":""},"AValue1":"a","AValue2":"b","AValue3":1,"MyObj2List":[{"ClassName":"TMyObj2","Testing":"My Obj Item "},{"ClassName":"TMyObj2","Testing":"My Obj Item 2"}]},{"ClassName":"TMyObject","MyObj2":{"ClassName":"TMyObj2","Testing":"Testing 2"},"NextLevel":"ClassName":"TNextLevel","NextLevelDesc":"","TopLevelDesc":""},"AValue1":"c","AValue2":"d","AValue3":2,"MyObj2List":[]}]
===================================================
But will always return not valid as it doesn't know what class to use in the MyObjList2 field.
Thanks
Sorry, you're right.
I did test with that but failed, but worked ok now.
My apologies. Thanks - I think I needed to have ALL objects descend from TPersistentWithCustomCreate not just the nested object. (in this case TMyObject).
Cheers
Thanks...
I'm on 1.18.273 according to SynopseCommit.inc. I just did a pull now (I use git repository).
Just tried again but same result.
Yeah I did descend from TPersistentWithCustomCreate - I probably posted code after I'd played around a bit...
I'm using version 1.18
Thanks
With this sample code, why is it not automatically able to deserialize json string which has been successfully serialized with opposite function?
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Contnrs, Classes,
SynCommons, mORMot;
type
{$M+}
TMyObj2 = class(TPersistent)
private
FTesting: string;
public
constructor Create; virtual;
published
property Testing: string read FTesting write FTesting;
end;
{$M-}
{$M+}
TMyObject = class(TPersistent)
private
FAValue1: string;
FAValue2: string;
FAValue3: Integer;
FMyObj2: TMyObj2;
public
constructor Create; virtual;
function SetCreate(AValue1, AValue2: string; AValue3: Integer): TMyObject;
published
destructor Destroy; override;
property MyObj2: TMyObj2 read FMyObj2;
property AValue1: string read FAValue1 write FAValue1;
property AValue2: string read FAValue2 write FAValue2;
property AValue3: Integer read FAValue3 write FAValue3;
end;
{$M-}
{ TMyObj2 }
constructor TMyObj2.Create;
begin
inherited;
end;
{ TMyObject }
constructor TMyObject.Create;
begin
FMyObj2 := TMyObj2.Create;
end;
function TMyObject.SetCreate(AValue1, AValue2: string; AValue3: Integer): TMyObject;
begin
FAValue1 := AValue1;
FAValue2 := AValue2;
FAValue3 := AValue3;
Result := Self;
end;
destructor TMyObject.Destroy;
begin
FMyObj2.Free;
inherited;
end;
var
List: TObjectList;
Json: RawUTF8;
List2: TObjectList;
Valid: Boolean;
From: PUTF8Char;
Obj: TMyObject;
Obj2: TObject;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
Obj := tMyObject.Create.SetCreate('a','b',1);
try with
Json := ObjectToJson(Obj, [woStoreClassName]);
Writeln(Utf8ToString(Json));
Writeln('===================================================');
From := @Json[1];
Obj2 := JsonToNewObject(From, Valid, []);
if not Valid then
Writeln('not valid');
if assigned(Obj2) then
writeln(obj2.ClassName)
else
writeln('unassigned');
finally
Obj.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
It correctly creates the Json string with the nested object, but when I attempt to pass that exact same json string back into the deserializer, it claims it's not valid. I can't find anywhere in the documentation talking about this.
Thanks
Hi
Just started looking into this framework.
For now, I am trying to serialize an object (ie descendant of TObject) into JSON (and back again).
It seems that the default (basic) methods - eg ObjectToJSON - only work against Published items. Is that true? Do I really need to make all my properties published to have them automatically serialized by the default parser? Or if I don't want to do that, do I have to create my own custom Reader/Writer methods to do the serialization for me? Seems a lot of work with the advanced RTTI nowadays.
Thanks!
Pages: 1