3
votes

I am working a custom class to xml converter and one of the requirements is the ability to stream TObjectList<T> fields.
I am trying to invoke the ToArray() method to get hold of the TObjectlist's objects, but I get 'Invalid class typecast' because the types obviously don't match.

take this class for example:

type
  TSite = class
    Name : String;
    Address : String; 
  end;

  TSites = class
    Sites : TObjecList<TSite>;
  end;  

I just need to get the Site Objects from the Sites TObjectList. Please keep in mind that I am using RTTI, so I don't know the ObjectType in TObjectList, so Typecasting won't work. This is what I have but it seems a dead end (Obj is TobjectList<TSite> here):

function TXmlPersister.ObjectListToXml(Obj : TObject; Indent: String): String;

var
  TypInfo: TRttiType;
  meth: TRttiMethod;
  Arr  : TArray<TObject>;

begin
 Result := '';
 TypInfo := ctx.GetType(Obj.ClassInfo);
 Meth := TypInfo.GetMethod('ToArray');
 if Assigned(Meth) then
  begin
   Arr := Invoke(Obj, []).AsType<TArray<TObject>>; // invalid class typecast error

   if Length(Arr) > 0 then
    begin
     // get objects from array and stream them
     ...
    end;
  end;

Any way to get the objects out of the TObjectList via RTTI is good for me. For some odd reason I don't see the GetItem/SetItem methods in TypInfo

EDIT

Thanks to David I have my solution:

function TXmlPersister.ObjectListToXml(Obj : TObject; Indent: String): String;

var
  TypInfo: TRttiType;
  meth: TRttiMethod;
  Value: TValue;
  Count : Integer;

begin
 Result := '';
 TypInfo := ctx.GetType(Obj.ClassInfo);
 Meth := TypInfo.GetMethod('ToArray');
 if Assigned(Meth) then
  begin
   Value := Meth.Invoke(Obj, []);
   Assert(Value.IsArray);
   Count :=  Value.GetArrayLength;
   while Count > 0 do
    begin
     Dec(Count);
     Result := Result + ObjectToXml(Value.GetArrayElement(Count).AsObject, Indent);
    end;
  end;
end;

I am open for suggestions, maybe there are more 'clever' ways to achieve this goal...

1
Why do you need to put the items in an array? Could you not just call GetItem instead to read each item one by one? You can store the result in a TObject without having to know T.Rob Kennedy
@RobKennedy, for some reason I don't find the method when iterating over typeinfo.GetMethods();whosrdaddy

1 Answers

4
votes

Your code fails because a dynamic array is not a TObject.

You can do it like this:

Value := Meth.Invoke(Obj, []);
Assert(Value.IsArray);
SetLength(Arr, Value.GetArrayLength);
for i := 0 to Length(Arr)-1 do
  Arr[i] := Value.GetArrayElement(i).AsObject;