Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EInsufficientRTTI exception with message 'Insufficient RTTi available to support this operation'

Trying to convert an object to JSON at run time, I'm getting insufficient RTTI Error. The object is:

{$M+}
{$TYPEINFO ON}
{$METHODINFO ON}
{$RTTI EXPLICIT METHODS([vcPublic, vcPublished]) PROPERTIES([vcPublic, vcPublished])}

TMyPacket = class(TObject)
  Private
    FID: TGUID;
    FToIP: string;
    FToPort: integer;
    FSent: boolean;
    FSentAt: TDateTime;
    FAck: boolean;
    FTimeOut: Cardinal;
    FDataToSendSize: UINT64;
    FDataToSend: AnsiString;
  public
    constructor create;
    destructor free;
  published
    property ID: TGUID read FID write FID;
    property ToIP: string read FToIP write FToIP;
    property ToPort: integer read FToPort write FToPort;
    property Sent: boolean read FSent write FSent;
    property SentAt: TDateTime read FSentAt write FSentAt;
    property Ack: boolean read FAck write FAck;
    property TimeOut: Cardinal read FTimeOut write FTimeOut;
    property DataToSend: AnsiString read FDataToSend write FDataToSend;
  end;

later in the code:

var fpacket: TMYPacket;
begin     
fpacket := TVWTCPPacket.create;
//assign value to class properties
memo1.Lines.Text := TJson.ObjectToJsonString(fpacket); //throws error

and getting the error on JSON conversion. Does anyone have any idea on whats going wrong with Delphi Berlin 10.1? I do remember some RTTI issues on XE2 but not sure what is happening above is related to Delphi old bug or not.

like image 804
LastManStanding Avatar asked Mar 10 '23 22:03

LastManStanding


1 Answers

Go into System.Rtti and put a breakpoint into TRttiField.GetValue at the line if ft = nil then and put a breakpoint condition ft = nil (if you would put the breakpoint in the next line you would not be able to see what the Name of the field was because of optimization).

When your debugger stops there you can inspect Self.Name (Ctrl+F7) and it will tell you D4 which is the fourth field of your TGuid. This is because JsonReflect recursively serializes records by serializing their fields. Because D4 is declared as array[0..7] of Byte it does not contain type info (see also).

To solve this create your own type interceptor for TGUID:

type
  TGuidInterceptor = class(TJSONInterceptor)
  public
    function StringConverter(Data: TObject; Field: string): string; override;
    procedure StringReverter(Data: TObject; Field: string; Arg: string); override;
  end;

function TGuidInterceptor.StringConverter(Data: TObject;
  Field: string): string;
var
  ctx: TRttiContext;
begin
  Result := ctx.GetType(Data.ClassInfo).GetField(Field).GetValue(Data).AsType<TGuid>.ToString;
end;

procedure TGuidInterceptor.StringReverter(Data: TObject; Field, Arg: string);
var
  ctx: TRttiContext;
begin
  ctx.GetType(Data.ClassInfo).GetField(Field).SetValue(Data, TValue.From(TGuid.Create(Arg)));
end;

and mark the field accordingly:

[JsonReflect(ctString, rtString, TGuidInterceptor)]
FID: TGUID;
like image 192
Stefan Glienke Avatar answered Apr 30 '23 07:04

Stefan Glienke