#1 mORMot 1 » Equivalent of RecordLoadJSON and RecordSaveJSON for mobile clients » 2021-07-04 10:04:09

jsmart
Replies: 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;

#2 mORMot 1 » Deserialize nested TObjectList » 2014-11-12 11:20:27

jsmart
Replies: 3

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

#3 Re: mORMot 1 » Can't deserialize with nested object » 2014-09-25 10:04:41

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

#4 Re: mORMot 1 » Can't deserialize with nested object » 2014-09-21 11:04:47

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.

#5 Re: mORMot 1 » Can't deserialize with nested object » 2014-09-21 07:30:43

Yeah I did descend from TPersistentWithCustomCreate  - I probably posted code after I'd played around a bit...

I'm using version 1.18

Thanks

#6 mORMot 1 » Can't deserialize with nested object » 2014-09-21 03:43:54

jsmart
Replies: 7

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

#7 mORMot 1 » Serialize non-published items without custom reader/writer » 2014-09-19 01:49:44

jsmart
Replies: 1

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!

Board footer

Powered by FluxBB