Watch, Follow, &
Connect with Us

Please visit our new home

Welcome, Guest
Guest Settings

Thread: Rtti to deserialize JsonArray to TObjectList<T>

Permlink Replies: 0 Threads: [ Previous | Next ]
Clement Doss

Posts: 133
Registered: 9/19/00
Rtti to deserialize JsonArray to TObjectList<T>
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 28, 2018 3:55 PM

I'm using Delphi Berlin/Tokyo and I need to Deserialize some classes. My routines are working fine, but I would like to find a better way to populate TObjectList<T>.
This is the code I'd like to improve.

procedure dhsJsArrayToGenericList(  aPropType : TRttiType; aJsonValue : TJsonValue; var aPropertyValue : TValue ); overload;
  i : integer;
  lJS : TJsonArray;
  lElementValue : TValue;
  lElementType : TRttiType;
  lElementArray  : TValue;
  lAddMethod : TRttiMethod;
  // TList<T> or TObjectList<T> is created
   if not (aJsonValue is TJSONNull) then begin
      lJS := TJSONArray( aJsonValue );
      lAddMethod := aPropType.GetMethod('AddRange');
      lElementArray := aPropType.GetMethod('ToArray').Invoke(aPropertyValue,[]);
      lElementArray := dhsJsonArrayToDynArray( lCTX.GetType( lElementArray.TypeInfo ), lJS  );
      lAddMethod.Invoke(aPropertyValue,[lElementArray]); // ##1
      lAddMethod := aPropType.GetMethod('Add');
      lElementtype := dhsGenericListItemType(aPropType.Handle^.Name);
      for i := 0 to lJS.Count-1 do begin
         lElementValue := dhsJsonValueToValue( lElementType, lJs.Items[i]);

As you can see I'm invoking the Add method for every class. This is painfully slow. I thought It would be better to use AddRange. I just needed to build a DynArray of T. Piece of cake!
But at ##1 the following exception is raised:

Debugger Exception Notification

Project SerializerTest.exe raised exception class EInvalidCast with message 'VAR and OUT arguments must match parameter type exactly'.

Break Continue Help

Following the System.Rtti, the exception is raised here:

      if Par.ParamType.Handle <> ArgSrc.TypeInfo then
        raise EInvalidCast.CreateRes(@SByRefArgMismatch);
      ArgDest := TValue.From(ArgSrc.GetReferenceToRawData);

The debugger shows that Par,ParamType.Handle is a (tkClass, 'TMyItem') and ArgSrc.TypeInfo is a (tkDynArray, 'TArray<frm.main.TMyItem>')

The declaration of TObjectList<T> (aka TList<T>) shows :
    procedure AddRange(const Values: array of T); overload;
    procedure AddRange(const Collection: IEnumerable<T>); overload; inline;
    procedure AddRange(const Collection: TEnumerable<T>); overload; inline;

I already looped through all the 'AddRange' RTTI methods. The first method has a 'TMyItem' parameters ( seems wrong to me). the second and third are showing Interface IEnumerable and Class TEnumerable.
Why does the RTTI switches the Array of TMyItem with TMyItem?
Note that "Function ToArray : TArray<T>" returns a dynamic array. But "Array of T" returns T. I did some testing and in fact RTTI completely ignores the type "Array Of T", but recognizes "TArray<T>". Nice heh??

What am I missing?

Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02