Reputation: 1387
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
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