#1 2015-11-18 14:10:21

ioda19
Member
Registered: 2015-11-18
Posts: 14

Unserialize JSON with complex TObjectList structure

Hi,

I search for many hours without found a solution
I have a complex structure of class with TObjectList.
I can serialize JSON from the object but if I unserialize to object, all my TObjectList have no data and isValid result if false
Could you help me

This is my code

CLASS DECLARATION

type
  TBaseClass = class(TSynAutoCreateFields)
  private
    FID: RawUTF8;
  published
    property ID : RawUTF8 read FID write FID;
end;

type
  TGroup = class(TBaseClass)
  private
    FName: RawUTF8;
  published
    property Name : RawUTF8 read FName write FName;
end;

type
  TGroups = class(TSynAutoCreateFields)
  private
    FGroupID: RawUTF8;
    FUserID: RawUTF8;
    FGroup: TGroup;
    function GetGroup: TGroup;
  published
    property UserID : RawUTF8 read FUserID write FUserID;
    property GroupID : RawUTF8 read FGroupID write FGroupID;
    property Group : TGroup read FGroup;
end;

type
  TUser = class(TBaseClass)
  private
    FUsername: RawUTF8;
    FFirstName: TNullableUTF8Text;
    FLastName: TNullableUTF8Text;
    FGroups: TObjectList;
    FCanSynchronize: boolean;
    function GetGroups(Index: Integer): TGroups;
  published
    property Username : RawUTF8 read FUsername write FUsername;
    property FirstName : TNullableUTF8Text read FFirstName write FFirstName;
    property LastName : TNullableUTF8Text read FLastName write FLastName;

    property Groups: TObjectList read FGroups;
end;

type
  TCompany = class(TBaseClass)
  private
    FCanSynchronize: boolean;
    FType: Integer;
    FName: RawUTF8;
  published
    property CanSynchronize : boolean read FCanSynchronize write FCanSynchronize;
    property Name : RawUTF8 read FName write FName;
    property Types : Integer read FType write FType;
end;

type
  TSession = class(TBaseClass)
  private
    FUserID: RawUTF8;
    FUser: TUser;
    FCompanyID: RawUTF8;
    FCompany: TCompany;
    function GetCompany: TCompany;
    function GetUser: TUser;
  published
    property UserID : RawUTF8 read FUserID write FUserID;
    property User : TUser read FUser;
    property CompanyID : RawUTF8 read FCompanyID write FCompanyID;
    property Company : TCompany read FCompany;
end;

type
  TSessions = class(TSynAutoCreateFields)
  private
    FSession: TSession;
    function GetSession: TSession;
  published
    property Session : TSession read FSession;
  end;

GENERATE JSON (WORK)

var
  lSessions : TSessions;
begin
  lSessions := TSessions.Create;

  lSessions.Session.UserID := ('d0d49a49-7cd2-4336-a85e-0bd60584178e');
  lSessions.Session.User.Username := 'admin';
  lSessions.Session.User.Groups.Add(TGroups.Create); //Add one group into JSON
  TGroups(lSessions.Session.User.Groups[0]).UserID := ('d0d49a49-7cd2-4336-a85e-0bd60584178e');
  TGroups(lSessions.Session.User.Groups[0]).Group.Name := 'TEST';

  lSessions.Session.CompanyID := ('a58907b7-8be2-4a14-9a82-0bd60581fad0');
  lSessions.Session.Company.Name := 'Acomba Demo';

  TJSONSerializer.RegisterClassForJSON([TSessions,TUser,TCompany,TGroups,TGroup]);

  Memo1.Text := ObjectToJSON(lSessions, [woHumanReadable, woObjectListWontStoreClassName]); 
  //I must to declare woObjectListWontStoreClassName because the REAL JSON come from another system and doesn't have ClassName field
  //But if I undeclare woObjectListWontStoreClassName deserialization work

  FreeAndNil(lSessions);

JSON RESULT

{
	"Session": {
		"UserID": "d0d49a49-7cd2-4336-a85e-0bd60584178e",
		"User": {
			"Username": "admin",
			"FirstName": null,
			"LastName": null,
			"Groups": 
			[{
					"UserID": "d0d49a49-7cd2-4336-a85e-0bd60584178e",
					"GroupID": "",
					"Group": {
						"Name": "TEST",
						"ID": ""
					}
				}
			],
			"ID": ""
		},
		"CompanyID": "a58907b7-8be2-4a14-9a82-0bd60581fad0",
		"Company": {
			"CanSynchronize": false,
			"Name": "Acomba Demo",
			"Types": 0,
			"ID": ""
		},
		"ID": ""
	}
}

LOAD JSON INTO OBJECT

var
  lSessions : TSessions;
  isValid : boolean;
begin
  lSessions := TSessions.Create;

  TJSONSerializer.RegisterClassForJSON([TSessions,TUser,TCompany,TGroups,TGroup]);

  JSONToObject(lSessions, @Memo1.Text[1], isValid, TSessions);
  FreeAndNil(lSessions);

My question is, could I use TObjectList without ClassName field into JSON ?

Or how I can change my structure with arrays at least TObjectList ?

Thank you

Offline

#2 2015-11-18 17:39:38

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Unserialize JSON with complex TObjectList structure

It is not possible, since the TObjectList property has no RTTI information.

What you may do is use a TGroupsObjArray = array of TGroups instead of the TObjectList for storing the array.

Then, serialization should work as expected.

Offline

#3 2015-11-18 18:13:05

ioda19
Member
Registered: 2015-11-18
Posts: 14

Re: Unserialize JSON with complex TObjectList structure

Ok thank you
That was I expected

I'll ready try with TGroupsArray= array of TGroups but not work when deserialize
My array is always empty even I tried TJSONSerializer.RegisterObjArrayForJSON(TypeInfo(TGroupsArray),TGroups);

Can you give me an exemple with array and unserialize ?

Thank

Offline

#4 2015-11-18 20:18:37

ioda19
Member
Registered: 2015-11-18
Posts: 14

Re: Unserialize JSON with complex TObjectList structure

I found solution with Collection

This is my code for someone need it

CLASS DEFINITION

type
  TBaseClass = class(TSynAutoCreateFields)
  private
    FID: RawUTF8;
  published
    property ID : RawUTF8 read FID write FID;
end;

type
  TGroup = class(TBaseClass)
  private
    FName: RawUTF8;
  published
    property Name : RawUTF8 read FName write FName;
end;

type
  //CHANGE CLASS TYPE HERE FOR TCollectionItemAutoCreateFields
  TGroups = class(TCollectionItemAutoCreateFields)
  private
    FGroupID: RawUTF8;
    FUserID: RawUTF8;
    FGroup: TGroup;
  published
    property UserID : RawUTF8 read FUserID write FUserID;
    property GroupID : RawUTF8 read FGroupID write FGroupID;
    property Group : TGroup read FGroup;
end;       

//THIS IS NEW CLASS FOR MANAGE COLLECTION AND ACCESS LIKE AN ARRAY
type
  TGroupsCollection = class(TInterfacedCollection)
  private
    function GetCollItem(aIndex: Integer): TGroups;
  protected
    class function GetClass: TCollectionItemClass; override;
  public
    function Add: TGroups;
    property Item[aIndex: Integer]: TGroups read GetCollItem; default;
  end;

type
  TUser = class(TBaseClass)
  private
    FUsername: RawUTF8;
    FFirstName: TNullableUTF8Text;
    FLastName: TNullableUTF8Text;
    FGroups: TGroupsCollection;
    FCanSynchronize: boolean;
  published
    property Username : RawUTF8 read FUsername write FUsername;
    property FirstName : TNullableUTF8Text read FFirstName write FFirstName;
    property LastName : TNullableUTF8Text read FLastName write FLastName;

    property Groups: TGroupsCollection read FGroups;
end;

type
  TCompany = class(TBaseClass)
  private
    FCanSynchronize: boolean;
    FType: Integer;
    FName: RawUTF8;
  published
    property CanSynchronize : boolean read FCanSynchronize write FCanSynchronize;
    property Name : RawUTF8 read FName write FName;
    property Types : Integer read FType write FType;
end;

type
  TSession = class(TBaseClass)
  private
    FUserID: RawUTF8;
    FUser: TUser;
    FCompanyID: RawUTF8;
    FCompany: TCompany;
  published
    property UserID : RawUTF8 read FUserID write FUserID;
    property User : TUser read FUser;
    property CompanyID : RawUTF8 read FCompanyID write FCompanyID;
    property Company : TCompany read FCompany;
end;

type
  TSessions = class(TSynAutoCreateFields)
  private
    FSession: TSession;
    function GetSession: TSession;
  published
    property Session : TSession read FSession;
  end;

CODE FOR CLASS TGroupsCollection

function TGroupsCollection.Add: TGroups;
begin
  result := TGroups(inherited Add);
end;

class function TGroupsCollection.GetClass: TCollectionItemClass;
begin
  result := TGroups;
end;

function TGroupsCollection.GetCollItem(aIndex: Integer): TGroups;
begin
  result := TGroups(GetItem(aIndex));
end;

CREATE JSON FILE

  lSessions := TSessions.Create;

  lSessions.Session.UserID := ('d0d49a49-7cd2-4336-a85e-0bd60584178e');
  lSessions.Session.User.Username := 'admin';

  with lSessions.Session.User.Groups.Add do
  begin
    Group.Name := 'ADMIN';
  end;
  with lSessions.Session.User.Groups.Add do
  begin
    Group.Name := 'INVITE';
  end;

  lSessions.Session.CompanyID := ('a58907b7-8be2-4a14-9a82-0bd60581fad0');
  lSessions.Session.Company.Name := 'Acomba Demo';

  Memo1.Text := ObjectToJSON(lSessions, [woHumanReadable]);

  FreeAndNil(lSessions);

JSON CREATED

{
	"Session": {
		"UserID": "d0d49a49-7cd2-4336-a85e-0bd60584178e",
		"User": {
			"Username": "admin",
			"FirstName": null,
			"LastName": null,
			"Groups": 
			[{
					"UserID": "",
					"GroupID": "",
					"Group": {
						"Name": "ADMIN",
						"ID": ""
					}
				},{
					"UserID": "",
					"GroupID": "",
					"Group": {
						"Name": "INVITE",
						"ID": ""
					}
				}
			],
			"ID": ""
		},
		"CompanyID": "a58907b7-8be2-4a14-9a82-0bd60581fad0",
		"Company": {
			"CanSynchronize": false,
			"Name": "Acomba Demo",
			"Types": 0,
			"ID": ""
		},
		"ID": ""
	}
}

UNSERIALIZE JSON INTO OBJECT

lSessions := TSessions.Create;
JSONToObject(lSessions, @Memo1.Text[1], isValid, nil, [j2oIgnoreUnknownProperty]);
FreeAndNil(lSessions);

Now you can access Groups like this : lSessions.session.user.groups[0].Group.Name or lSessions.session.user.groups.item[0].Group.Name

Offline

#5 2015-11-18 20:25:45

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Unserialize JSON with complex TObjectList structure

Have you some simple .dpr to show what you did?

You can send it to webcontact01 at synopse dot info.

Offline

#6 2015-11-19 15:55:02

ioda19
Member
Registered: 2015-11-18
Posts: 14

Re: Unserialize JSON with complex TObjectList structure

Sure

I'll email it today.
Just for your information, I base ma code on "20 - DTO interface based service" exemple

Offline

#7 2015-11-23 20:34:49

ioda19
Member
Registered: 2015-11-18
Posts: 14

Re: Unserialize JSON with complex TObjectList structure

Maybe I'll need to use array at least of CollectionItem

Could you give me a little exemple to use array in class with my class structure ?

Thank you

Offline

#8 2016-02-17 00:01:10

dzun
Member
Registered: 2016-02-16
Posts: 1

Re: Unserialize JSON with complex TObjectList structure

Is it possible for the to skip serialization for objects with no values assigned to them with this model using TSynPersistent?

Last edited by dzun (2016-02-17 00:03:12)

Offline

#9 2016-02-17 16:48:20

ioda19
Member
Registered: 2015-11-18
Posts: 14

Re: Unserialize JSON with complex TObjectList structure

I think if you set j2oIgnoreUnknownProperty in JSONToObject option will skip item if you don't have it in your class

Offline

#10 2017-09-06 10:06:49

linbren
Member
Registered: 2017-08-11
Posts: 42

Re: Unserialize JSON with complex TObjectList structure

in current version , it is still need use array  instead of the TObjectList for storing the array?


Windows 7 64bit. Delphi XE10.2 Professional.

Offline

#11 2017-09-06 10:55:52

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Unserialize JSON with complex TObjectList structure

TObjectList has no type information about the actual class it stores.
TObjectList<> generic version neither (even on newer versions of the compiler, Delphi RTTI is broken in this aspect).

This is why T*ObjArray, and its associated ObjArray*() functions, is a good way of storing a list of objects.
For both SOA and ORM.

Offline

#12 2017-09-07 00:15:56

linbren
Member
Registered: 2017-08-11
Posts: 42

Re: Unserialize JSON with complex TObjectList structure

I was try to use TObjectList<T> generic version ,  can not find a way to register the class T .
I suppose that , if there is a way to notice the framework which CLASS to convert , that must be work .
generic type is a trend ..


Windows 7 64bit. Delphi XE10.2 Professional.

Offline

#13 2017-09-07 06:50:04

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Unserialize JSON with complex TObjectList structure

As I wrote, TObjectList<T> is somewhat broken - or at least difficult to work with when serializing objects.

Offline

#14 2017-09-07 07:48:39

linbren
Member
Registered: 2017-08-11
Posts: 42

Re: Unserialize JSON with complex TObjectList structure

serializing seem work pretty good, difficult part is deserializing  smile


Windows 7 64bit. Delphi XE10.2 Professional.

Offline

#15 2017-09-07 11:54:23

emk
Member
Registered: 2013-10-24
Posts: 96

Re: Unserialize JSON with complex TObjectList structure

/sub

I have hard time also to deserialize into TObjectList. For me, I can't use TCollectionItem or array.
There is a possibility to register the class so the deserialize have enough info to produce TObjectList of T, or TObjectList<T>  even it's only for Windows?
Or can you implement something like this? Even if will not support old versions of Delphi or FPC. I think will be high use for many people with specific case and I think in time FPC will catch up with generics.
Or can be simple TObjectList and the user provide the class as parameter or something like this.

Thank you.

Offline

#16 2017-09-07 13:23:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,207
Website

Re: Unserialize JSON with complex TObjectList structure

Plain TObjectList doesn't support enough information for deserialization.
It is a dead end.

What could be done, is register TObjectList<T> with its plain T class...
Any commit is welcome.

Offline

#17 2017-09-08 05:25:42

Chaa
Member
Registered: 2011-03-26
Posts: 244

Re: Unserialize JSON with complex TObjectList structure

I use the following code and it's worked fine:

procedure TMainForm1.Refresh;
var
    LTable: RawJSON;
    LObjectList: TObjectList;
    LValid: Boolean;
begin
    ...
    LObjectList := TObjectList.Create();
    try
        JSONToObject(LObjectList, PUTF8Char(LTable), LValid, TSQLMyRecord);
        ...
    finally
        LObjectList.Free();
    end;
end;

Note the last parameter to JSONToObject.

Offline

#18 2017-09-08 08:19:17

linbren
Member
Registered: 2017-08-11
Posts: 42

Re: Unserialize JSON with complex TObjectList structure

@Chaa which version you are using ?

raising error ... Invalid variant op


Windows 7 64bit. Delphi XE10.2 Professional.

Offline

#19 2017-09-08 09:59:28

Chaa
Member
Registered: 2011-03-26
Posts: 244

Re: Unserialize JSON with complex TObjectList structure

I use 1.18.3759, but it should work with latest mORMot version.

It's documented at:
https://synopse.info/files/html/api-1.1 … ONTOOBJECT

- won't handle TObjectList (even if ObjectToJSON is able to serialize them) since has no way of knowing the object type to add (TCollection.Add is missing), unless:
1. you set the TObjectListItemClass property as expected, and provide a TObjectList object, or
2. woStoreClassName option has been used at ObjectToJSON() call and the corresponding classes have been previously registered by TJSONSerializer.RegisterClassForJSON() (or Classes.RegisterClass)

So, if you specify class for TObjectList items, that should work.

Offline

#20 2017-09-13 09:35:33

linbren
Member
Registered: 2017-08-11
Posts: 42

Re: Unserialize JSON with complex TObjectList structure

			JSONObject parse = (JSONObject) JSON.parse(resp);
			JSONArray itemArray = parse.getJSONArray("items");
			for (int i = 0; i < itemArray.size(); i++) {
				JSONObject item = itemArray.getJSONObject(i);
				System.out.println(item.getString("seqNo")
						+ item.getString("caseDate")
						+ item.getString("caseNumber")
						+ item.getString("court") + item.getString("amount"));
			}
{
    "total": 2, 
    "items": [
        {
            "seqNo": 1, 
            "caseDate": "2017-06-09", 
            "caseNumber": "1212", 
            "court": "court1", 
            "amount": "3.000000"
        }, 
        {
            "seqNo": 2, 
            "caseDate": "2017-06-09", 
            "caseNumber": "2233", 
            "court": "court2", 
            "amount": "12.000000"
        }
    ]
}

parse by jasonfast , no RTTI reqiured.


Windows 7 64bit. Delphi XE10.2 Professional.

Offline

Board footer

Powered by FluxBB