Reputation: 1968
I'm trying to serialize objects with TJSONMarshal (XE4) but I have a problem when objects has interfaces properties like _recordset
e.g.
My class:
TFoo = class
private
FrsFoo: _recordset;
FFooProp: integer;
published
property rsFoo: _recordset read FrsFoo write FrsFoo;
property FooProp: integer read FFooProp write FFooProp;
end;
My function:
function TestSerialize: string;
var
JsonMarshal: TJSONMarshal;
Foo: TFoo;
begin
JsonMarshal := TJSONMarshal.Create(TJSONConverter.Create);
Foo := TFoo.Create;
Result := JsonMarshal.Marshal(Foo).ToString;
end;
It results:
{"type":"uTest.TFoo","id":1,"fields":{"FFooProp":0}}
rsFoo isn't serialized!
Can I serialize it? or its a limitation of TJSONMarshal?
Upvotes: 2
Views: 1228
Reputation: 1968
In my case I just want to serialize _recordsets so my solution was:
1) Get all _Recordset type fields:
function Test.GetRecordsetFieldsFromObject(
AObject: TObject): TStringList;
var
Obj: TRttiType;
Rtti: TRTTIContext;
ObjField: TRttiField;
IntfObj: IInterface;
rsOut: _recordset;
begin
Result := TStringList.Create;
Obj := Rtti.GetType(AObject.ClassType);
for ObjField in Obj.GetFields do
if ObjField.FieldType.TypeKind = tkInterface then
begin
IntfObj := ObjField.GetValue(AObject).AsInterface;
if IntfObj.QueryInterface(_Recordset, rsOut) = 0 then
begin
Result.Add(ObjField.Name);
rsOut := nil;
end;
end;
end;
2) Register converter and reverter for each field founded
for FieldName in FieldNameList do
begin
JsonMarshal.RegisterConverter(TFoo, FieldName, function(Data: TObject; Field: String): TListOfStrings
var
Obj: TRttiType;
ObjField: TRttiField;
rsProp: _Recordset;
strStream: TStringStream;
begin
SetLength(Result, 1);
strStream := TStringStream.Create;
try
Obj := Rtti.GetType(data.ClassType);
ObjField := Obj.GetField(Field);
rsProp := ObjField.GetValue(Data).AsInterface as _Recordset;
rsProp.Save(TStreamAdapter.Create(strStream) as IUnknown, adPersistXML);
Result[0] := strStream.DataString;
finally
rsProp := nil;
strStream.Free;
end;
end);
JsonUnMarshal.RegisterReverter(TFoo, FieldName, procedure(Data: TObject; Field: String; Args: TListOfStrings)
var
Obj: TRttiType;
ObjField: TRttiField;
rsProp: _Recordset;
strStream: TStringStream;
begin
rsProp := coRecordset.Create;
strStream := TStringStream.Create(Args[0]);
try
Obj := Rtti.GetType(data.ClassType);
ObjField := Obj.GetField(Field);
strStream.Position := 0;
rsProp.Open(TStreamAdapter.Create(strStream) as IUnknown, EmptyParam, adOpenUnspecified, adLockUnspecified, 0);
ObjField.SetValue(Data, TValue.From<_Recordset>(rsProp));
finally
rsProp := nil;
strStream.Free;
end;
end);
end;
Upvotes: 1