Search code examples
jsondelphiserializationrecordsetdelphi-xe4

Can I serialize an interface(_recordset) with TJSONMarshal?


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?


Solution

  • 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;