Jump to content
Sign in to follow this  
Larry Hengen

Serialize/Deserialize Enums with no RTTI

Recommended Posts

Posted (edited)

I am trying to figure out the best way to deal with a REST API that has rather large and numerous codes that are mapped to Enums in Delphi.  Unfortunately, Delphi doesn't automatically handle the enums because they are assigned values so no RTTI is generated.  For Example:

TReportTypeCode = (reportTypeCodeSTR=102, reportTypeCodeLCTR=106, reportTypeCodeCDR=113,  reportTypeCodeLVCTR=14, reportTypeCodeEFTR=145);

 

I have enabled Scoped_Enums to prevent confusion between the many similarly named enums and eliminate the convention of using a neumonic prefix.  Since the object graph is deep, with many such enum values I would like to find a way to serialize/deserialize the root object without using TJSONWriter/Reader and coding the entire de/serialization.

 

Is it possible to use Interceptors with the native Delphi JSON libraries?

 

Are there any other third party (preferably open source) libraries that can handle such enums?

 

Edited by Larry Hengen
add tags

Share this post


Link to post

Hello,

I learned something new as I didn't know such types do not have RTTI. Any property of such type is hidden when you try to get the properties from a object. This makes it impossible to do automatic marshaling.

 

Delphi does support custom marshalling using TJSONMarshal (Serializing User Objects - RAD Studio (embarcadero.com)) that allows registration of marshaling functions: procedure RegisterConverter(clazz: TClass; const func: TTypeObjectConverter);

 

Another option would be to add attributes to your classes to identify these properties and then write your own marshaller that looks at these attributes:

 

Something like this should work:

  TEnum1 = (Value1=20, Value2=40);

  TEnum1Helper = record helper for TEnum1
    function ToString() : string;
  end;

  [TJsonEnumproperty('En', UnmarshalFunc, MarshalFunc)]
  TObjectWithhEnum = class
  private
    _id: Integer;
    _en: TEnum1;

  published
    property ID: Integer read _id write _id;
    property En: TEnum1 read _en write _en;
  end;

Your marshal method then looks at the attributes defined for the class and then call the MarshalFunc/UnmarshalFunc.

 

Alternatively you can use interfaces to achieve the same. 

  ICustomMarshalling = interface
    ['{E799D069-66B8-464D-B718-78D5D4DB6747}']
    procedure Marshal(M: TJsonMarshal);
    procedure UnMarshal(M: TJsonUnMarshal);
  end;

  TObjectWithhEnum = class(TObject, ICustomMarshalling)
  private
    _id: Integer;
    _en: TEnum1;

  proteced
    procedure Marshal(M: TJsonMarshal);
    procedure UnMarshal(M: TJsonUnMarshal);

  published
    property ID: Integer read _id write _id;
    property En: TEnum1 read _en write _en;
  end;

In this scenario your custom marshaller checks if your class implements the ICustomMarshalling interface and calls the appropriate methods.

 

Like to see what solution you come up with...

 

 

Share this post


Link to post

I know, that probably won't solve your problem, but I usually get rid of these kind of enumerations in favor of proper ones supported by RTTI. The numerical values are handles by record helpers:

type
  TReportTypeCode = (reportTypeCodeSTR, reportTypeCodeLCTR, reportTypeCodeCDR,  reportTypeCodeLVCTR, reportTypeCodeEFTR);
  TReportTypeCodeHelper = record helper for TReportTypeCode
  private
  const
    cMapping: array[TReportTypeCode] of Integer = (102, 106, 113, 14, 145);
    function GetAsInteger: Integer;
    procedure SetAsInteger(const Value: Integer);
  public
    property AsInteger: Integer read GetAsInteger write SetAsInteger;
  end;

function TReportTypeCodeHelper.GetAsInteger: Integer;
begin
  Result := cMapping[Self];
end;

procedure TReportTypeCodeHelper.SetAsInteger(const Value: Integer);
begin
  for var idx := Low(Self) to High(Self) do begin
    if cMapping[idx] = Value then begin
      Self := idx;
      Exit;
    end;
  end;
  raise EInvalidOperation.Create('invalid Integer value for TReportTypeCode');
end;

Now there is no more casting to and from Integers. 

  • Like 7

Share this post


Link to post

What I ended up doing was using Neon for serialization because it seems to be easier to create custom serializers than the stock framework.  I used Uwe's approach above, so my enums have RTTI which Neon also requires.  The Neon serializer then just calls the record helper method to substitute the proper integer value for the enum.

 

Thanks to both of you for responding.  Your suggestions were quite helpful.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×