In addition to answering this question, I posted a workaround to your previous question here: Generics and Marshal / UnMarshal. What am I missing here?
For some reason, using the non-standard TJsonobject type constructor causes a problem in XE2 β using the default constructor "fixes" the problem.
Firstly, you need to transfer your TTestobject to your own module - otherwise RTTI will not be able to find / create its object when trying to cancel the exchange.
unit uTestObject; interface uses SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections, DbxJson, DbxJsonReflect; type {$RTTI EXPLICIT METHODS([]) PROPERTIES([vcPublished]) FIELDS([vcPrivate])} TTestObject=class(TObject) private aList:TStringList; public constructor Create; overload; constructor Create(list: array of string); overload; constructor Create(list:TStringList); overload; destructor Destroy; override; function Marshal:TJSonObject; class function Unmarshal(value: TJSONObject): TTestObject; published property List: TStringList read aList write aList; end; implementation { TTestObject } constructor TTestObject.Create; begin inherited Create; aList:=TStringList.Create; end; constructor TTestObject.Create(list: array of string); var I:Integer; begin Create; for I:=low(list) to high(list) do begin aList.Add(list[I]); end; end; constructor TTestObject.Create(list:TStringList); begin Create; aList.Assign(list); end; destructor TTestObject.Destroy; begin aList.Free; inherited; end; function TTestObject.Marshal:TJSonObject; var Mar:TJSONMarshal; begin Mar:=TJSONMarshal.Create(); try Mar.RegisterConverter(TStringList, function(Data:TObject):TListOfStrings var I, Count:Integer; begin Count:=TStringList(Data).Count; SetLength(Result, Count); for I:=0 to Count-1 do Result[I]:=TStringList(Data)[I]; end); Result:=Mar.Marshal(Self) as TJSonObject; finally Mar.Free; end; end; class function TTestObject.Unmarshal(value: TJSONObject): TTestObject; var Mar: TJSONUnMarshal; L: TStringList; begin Mar := TJSONUnMarshal.Create(); try Mar.RegisterReverter(TStringList, function(Data: TListOfStrings): TObject var I, Count: Integer; begin Count := Length(Data); Result:=TStringList.Create; for I := 0 to Count - 1 do TStringList(Result).Add(string(Data[I])); end ); //UnMarshal will attempt to create a TTestObject from the TJSONObject data //using RTTI lookup - for that to function, the type MUST be defined in a unit Result:=Mar.UnMarshal(Value) as TTestObject; finally Mar.Free; end; end; end.
Also note that the constructor is overloaded - this allows you to see that the code functions without preliminary processing of data in the object during creation.
Here is an implementation for a generic class list object
unit uTestObjectList; interface uses SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections, DbxJson, DbxJsonReflect, uTestObject; type {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])} TTestObjectList<T:TTestObject,constructor> = class(TObjectList<T>) public function Marshal: TJSonObject; constructor Create; class function Unmarshal(value: TJSONObject): TTestObjectList<T>; static; end; //Note: this MUST be present and initialized/finalized so that //delphi will keep the RTTI information for the generic class available //also, it MUST be "project global" - not "module global" var X:TTestObjectList<TTestObject>; implementation { TTestObjectList<T> } constructor TTestObjectList<T>.Create; begin inherited Create; //removed the add for test data - it corrupts unmarshaling because the data is already present at creation end; function TTestObjectList<T>.Marshal: TJSonObject; var Marshal: TJsonMarshal; begin Marshal := TJSONMarshal.Create; try Marshal.RegisterConverter(TTestObjectList<T>, function(Data: TObject): TListOfObjects var I: integer; begin SetLength(Result,TTestObjectlist<T>(Data).Count); for I:=0 to TTestObjectlist<T>(Data).Count-1 do Result[I]:=TTestObjectlist<T>(Data)[I]; end ); Result := Marshal.Marshal(Self) as TJSONObject; finally Marshal.Free; end; end; class function TTestObjectList<T>.Unmarshal(value: TJSONObject): TTestObjectList<T>; var Mar: TJSONUnMarshal; L: TStringList; begin Mar := TJSONUnMarshal.Create(); try Mar.RegisterReverter(TTestObjectList<T>, function(Data: TListOfObjects): TObject var I, Count: Integer; begin Count := Length(Data); Result:=TTestObjectList<T>.Create; for I := 0 to Count - 1 do TTestObjectList<T>(Result).Unmarshal(TJSONObject(Data[I])); end ); //UnMarshal will attempt to create a TTestObjectList<TTestObject> from the TJSONObject data //using RTTI lookup - for that to function, the type MUST be defined in a unit, //and, because it is generic, there must be a GLOBAL VARIABLE instantiated //so that Delphi keeps the RTTI information avaialble Result:=Mar.UnMarshal(Value) as TTestObjectList<T>; finally Mar.Free; end; end; initialization //force delphi RTTI into maintaining the Generic class information in memory x:=TTestObjectList<TTestObject>.Create; finalization X.Free; end.
There are several things that are important to note: If a universal class is created at runtime, the RTTI information is NOT stored if there is no public object reference to this class in memory. See here: Delphi: RTTI and TObjectList <TObject>
So, the unit described above creates such a variable and leaves it as an instance, as described in the related article.
The main procedure has been updated, which shows both marshaling and unmarshaling data for both objects:
procedure Main; var aTestobj, bTestObj, cTestObj : TTestObject; aList, bList : TTestObjectList<TTestObject>; aJsonObject, bJsonObject, cJsonObject : TJsonObject; s: string; begin aTestObj := TTestObject.Create(['one','two','three','four']); aJsonObject := aTestObj.Marshal; s:=aJsonObject.ToString; Writeln(s); bJsonObject:=TJsonObject.Create; bJsonObject.Parse(BytesOf(s),0,length(s)); bTestObj:=TTestObject.Unmarshal(bJsonObject) as TTestObject; writeln(bTestObj.List.Text); writeln('TTestObject marshaling complete.'); readln; aList := TTestObjectList<TTestObject>.Create; aList.Add(TTestObject.Create(['one','two'])); aList.Add(TTestObject.Create(['three'])); aJsonObject := aList.Marshal; s:=aJsonObject.ToString; Writeln(s); cJSonObject:=TJsonObject.Create; cJSonObject.Parse(BytesOf(s),0,length(s)); bList:=TTestObjectList<TTestObject>.Unmarshal(cJSonObject) as TTestObjectList<TTestObject>; for cTestObj in bList do begin writeln(cTestObj.List.Text); end; writeln('TTestObjectList<TTestObject> marshaling complete.'); Readln; end;