Para
Para

Reputation: 1387

How to convert TClass to T?

I use RTTI (SuperObject) to convert JSON to object:

class function RecordJson.Json2Record<T>(const obj: ISuperObject): T;
var
  ctx: TSuperRttiContext;
begin
  ctx := TSuperRttiContext.Create;
  try
    Result := ctx.AsType<T>(obj);
  finally
    ctx.Free;
  end;
end;

I use this way and it works:

if aSo.o['TDistanceBhTopConv'] <> nil then
  Result := RecordJson.Json2Record<TDistanceBhTopConv>(aSo.o['TDistanceBhTopConv']);

But I have a lot of classes. So I create a TDictionary to record the relation of string and class, and now I want to use the following code:

FClassDic: TDictionary<string, TClass>;
FClassDic.Add('TDistanceValTopConv', TDistanceValTopConv);
FClassDic.Add('TDistanceBhTopConv', TDistanceBhTopConv);
FClassDic.Add('TLbXsConv', TLbXsConv);
FClassDic.Add('TConcreteConv', TConcreteConv);
for Key in FClassDic.Keys do
  if aSo.o[Key] <> nil then
  begin
    Result := RecordJson.Json2Record<FClassDic.Items[Key]>(aSo.o[Key]);
  end;

But it can't compile:

E2250 There is no overloaded version of 'Json2Record' that can be called with these arguments

I know this is because of the difference of TClass (class of instance) and T (instance).

Is there any way to deal this?

Upvotes: 1

Views: 381

Answers (1)

R. Hoek
R. Hoek

Reputation: 1106

You can try to create a dictionary with object creation procedures, but if it's really worth it...

type
  // Object construction function type
  TCreateObjectProc = reference to function(const ctx: TSuperRttiContext; obj: ISuperObject): TObject;

  // Dictionary specification
  FCreateDict: TDictionary<string, TCreateObjectProc>;

Then fill the dictionary with object constructor functions

  // Dictionary initialisation
  FCreateDict.Add('TDistanceValTopConv',
    function(const ctx: TSuperRttiContext; obj: ISuperObject): TObject
    begin
      Result := ctx.AsType<TDistanceValTopConv>(obj);
    end);

  FCreateDict.Add('TDistanceBhTopConv',
    function(const ctx: TSuperRttiContext; obj: ISuperObject): TObject
    begin
      Result := ctx.AsType<TDistanceBhTopConv>(obj);
    end);

  FCreateDict.Add('TLbXsConv',
    function(const ctx: TSuperRttiContext; obj: ISuperObject): TObject
    begin
      Result := ctx.AsType<TLbXsConv>(obj);
    end);

  FCreateDict.Add('TConcreteConv',
    function(const ctx: TSuperRttiContext; obj: ISuperObject): TObject
    begin
      Result := ctx.AsType<TConcreteConv>(obj);
    end);

And then use it using

  for Key in FCreateDict.Keys do
    if aSo.o[Key] <> nil then
    begin
      ctx := TSuperRttiContext.Create;
      try
        Result := FCreateDict.Items[Key](ctx, aSo.o[Key]);
      finally
        ctx.Free;
      end;
    end;

And when using the TSerializer<T: class> class of Remy:

type
  TSerializer<T: class> = class
  public
    class function Deserialize(const ctx: TSuperRttiContext; obj: ISuperObject): TObject;
  end;

class function TSerializer<T>.Deserialize(const ctx: TSuperRttiContext;
  obj: ISuperObject): TObject;
begin
  Result := ctx.AsType<T>(obj);
end;

Then the code regarding the dictionary initialization looks like this (which compiles using Delphi 10.3)

  FCreateDict.Add('TDistanceValTopConv', TSerializer<TDistanceValTopConv>.Deserialize);
  FCreateDict.Add('TDistanceBhTopConv', TSerializer<TDistanceBhTopConv>.Deserialize);
  FCreateDict.Add('TLbXsConv', TSerializer<TLbXsConv>.Deserialize);
  FCreateDict.Add('TConcreteConv', TSerializer<TConcreteConv>.Deserialize);

Upvotes: 1

Related Questions