#1 2017-12-02 16:28:05

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Record property serialization in Delphi < 2010, no type info?

 TRange = record
    Min, Max: Integer;
  end;

  TOffense = record
    Damage, AttackSpeed: TRange;
  end;

  TEnemy = class(TPersistent)
  private
    fEnabled: Boolean;
    fName: string;
    fOffense: TOffense;
  published
    property Enabled: Boolean read fEnabled write fEnabled;
    property Name: string read fName write fName;
    property Offense: TOffense read fOffense;
  end
 

Hello.
Can anyone help me to serialize objects of above class?

ObjectToJson skips the Offense property...

I tried to

const
__TRange = 'Min,Max Integer';
__TOffense  = Damage,AttackSpeed TRange';

TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TRange), __TRange);
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TOffense), __TOffense);

 

Compilation fails saying that TRange and TOffense have no type info.
I'm using Delphi 7. Is there really no easy way to serialize such a simple object?

Last edited by salowabicz (2017-12-02 16:42:06)

Offline

#2 2017-12-02 20:27:13

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Record property serialization in Delphi < 2010, no type info?

This is a bug/limitation of Delphi itself.
The record fields have not RTTI!

Check https://synopse.info/files/html/Synopse … ml#TITL_51

So don't use a TPersistent for the main object, but just another record.
It will work as expected.

Offline

#3 2017-12-02 22:34:36

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Re: Record property serialization in Delphi < 2010, no type info?

Thanks for response. I cannot use record as I'm storing the objects in TObjectList. I changed it to

TRange = record
    Min, Max: Integer;
  end;

  TOffense = record
    Damage, AttackSpeed: TRange;
  end;

  TEnemy = class(TPersistent)
  private
    fEnabled: Boolean;
    fName: string;
    fOffense: TOffense;
  published
    property Enabled: Boolean read fEnabled write fEnabled;
    property Name: string read fName write fName;
    property DamageMin: Integer read fOffense.Damage.Min;
    property DamageMax: Integer read fOffense.Damage.Max;
    property AttackSpeedMin: Integer read fOffense.AttackSpeed.Min;
    property AttackSpeedMax: Integer read fOffense.AttackSpeed.Max;
  end;

and it works.. but seems not elegant to me. What is the recommended way to serialize TObjectList of objects with record/custom type fields?

Last edited by salowabicz (2017-12-02 22:37:02)

Offline

#4 2017-12-03 02:22:14

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Record property serialization in Delphi < 2010, no type info?

See example of serializing record in "10.1.3.2.4. Text-based definition" smile

Offline

#5 2017-12-03 09:56:03

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Re: Record property serialization in Delphi < 2010, no type info?

I have read that and already tried it, see my first post. It doesn't work, because records doesn't have type info (at least in Delphi 7) as ab said.
I need to serialize TObjectList of objects of TEnemy class, which have a record property. Is there any way to do this, other than exposing each record field as a property or upgrading to Delphi 2010?

Last edited by salowabicz (2017-12-03 10:06:43)

Offline

#6 2017-12-03 11:53:49

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Record property serialization in Delphi < 2010, no type info?

Even on Delphi 2010, I'm not sure it will work.
The missing RTTI for record published properties has been fixed even after this revision.

For a TSQLRecord, you can use TSQLRecordProperties.RegisterCustomRTTIRecordProperty.
But for a plain class, you need custom serialization for the class.

Consider defining TEnemy not as a class, but as an object.
It will be easily serialized, and you will have methods to work with.

As a workaround, you may define the published property as a variant (storing the information as a TDocVariant object), with a public TOffense record property, and getter/setter methods.

Offline

#7 2017-12-03 13:36:17

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Record property serialization in Delphi < 2010, no type info?

In D7, you could try to define an additional ref-counted field within the record to force rtti generation for your simple record types.
See http://codeverge.com/embarcadero.delphi … pe/1040908

Offline

#8 2017-12-03 14:29:38

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Re: Record property serialization in Delphi < 2010, no type info?

ab wrote:

As a workaround, you may define the published property as a variant (storing the information as a TDocVariant object), with a public TOffense record property, and getter/setter methods.

  TRange = record
    Min, Max: Integer;
  end;

  TOffense = record
    Damage, AttackSpeed: TRange;
  end;

  TEnemy = class(TPersistent)
  private
    fEnabled: Boolean;
    fName: string; 
    function GetOffense: Variant;
    procedure SetOffense(Value: Variant);
  public
    _Offense: TOffense;
  published
    property Enabled: Boolean read fEnabled write fEnabled;
    property Name: string read fName write fName;
    property Offense: Variant read GetOffense write SetOffense;
  end;
  
  

function TEnemy.GetOffense: Variant;
begin
   Result := _Obj(['Damage',_Obj(['Min',_Offense.Damage.Min,'Max',_Offense.Damage.Min]),
		   'AttackSpeed',_Obj(['Min',_Offense.AttackSpeed.Min,'Max',_Offense.AttackSpeed.Max]]);
end;
  
procedure TEnemy.SetOffense(Value: Variant);
begin
   with _Json(Value) do
   begin
     _Offense.Damage.Min := Damage.Min;
     ...
   end;
end;

Did you mean something like this?

Last edited by salowabicz (2017-12-03 15:55:41)

Offline

#9 2017-12-03 14:57:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Record property serialization in Delphi < 2010, no type info?

Adding a ref-counted field in the record is not a good idea, since it will slow down everything.
And the root cause of the problem is that a record published property has no RTTI in the property field of the class, anyway. So adding a ref-counted field won't be enough.

The more I think about it, the more I suspect that one of the following workaround may be good enough:
- defining individual properties, from each field of the record (as you did);
- using a published property as a variant (storing the information as a TDocVariant object) or a RawJSON, with a public TOffense record property, and getter/setter methods;
- write a custom serializer/deserializer for the whole TEnemy class (more work).

Offline

#10 2017-12-03 15:26:03

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Record property serialization in Delphi < 2010, no type info?

Yes, for the variant version, it is something like that.

For RawJSON serialization, this may be a way of doing it:

  TRange = record
    Min, Max: Integer;
  end;
  TOffense = record
    Damage, AttackSpeed: TRange;
  end;
  TEnemy = class(TSynPersistent)
  private
    fEnabled: Boolean;
    fName: string;
    function GetOffense: RawJSON;
    procedure SetOffense(Value: RawJSON);
  public
    Off: TOffense;
  published
    property Enabled: Boolean read fEnabled write fEnabled;
    property Name: string read fName write fName;
    property Offense: RawJSON read GetOffense write SetOffense;
  end;

function TEnemy.GetOffense: RawJSON;
begin
  result := JSONEncode([
    'damage','{','min',Off.Damage.Min,'max',Off.Damage.Max,'}',
    'attackspeed','{','min',Off.AttackSpeed.Min,'max',Off.AttackSpeed.Max,'}']);
end;

procedure RangeFromJSON(out Range: TRange; JSON: PUTF8Char);
var V: TPUtf8CharDynArray;
begin
  JSONDecode(JSON, ['min', 'max'], V);
  if V=nil then
    exit; 
  Range.Min := GetInteger(V[0]);
  Range.Max := GetInteger(V[1]);
end;

procedure TEnemy.SetOffense(Value: RawJSON);
var V: TPUtf8CharDynArray;

begin
  JSONDecode(Value,['damage','attackspeed'],V,true);
  if V=nil then
    exit;
  RangeFromJSON(Off.Damage, V[0]);
  RangeFromJSON(Off.AttackSpeed, V[1]);
end;

(note that I've just added proper RawJSON support for class JSON serialization)

But of course, the variant seems more easy to write (also slightly slower).

Offline

#11 2017-12-03 16:32:09

ComingNine
Member
Registered: 2010-07-29
Posts: 294

Re: Record property serialization in Delphi < 2010, no type info?

ab wrote:

...
And the root cause of the problem is that a record published property has no RTTI in the property field of the class, anyway. So adding a ref-counted field won't be enough...

Many thanks for your knowledgeable comments !

However, for the "won't be enough" part, as shown in a trivial example which is compilable under D7 and a SO post, it seems that adding a ref-counted field will be enough to generate RTTI for the record, for other records that contain it, and for the class that somehow publishes the record. Could you comment about or a possible counter-example ? big_smile

Offline

#12 2017-12-03 19:21:14

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Re: Record property serialization in Delphi < 2010, no type info?

On a side note, adding dummy string to TRange was one of the first things I have tried. ObjectToJson still wouldn't parse it. Curious about ab's answer though.

I have one last question, as I ran into a different issue. I thought about using a TStringList field. Is it supported out of the box?

  TEnemy = class(TPersistent)
  private
    fEnabled: Boolean;
    fName: string;
    fList: TStringList;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property Enabled: Boolean read fEnabled write fEnabled;
    property Name: string read fName write fName;
    property List: TStringList read fList write fList;
  end;
  
destructor TEnemy.Destroy;
begin
   fList.Free;
end;

constructor TEnemy.Create;
begin
   fList := TStringList.Create;
end;

When using it like this:

var
 en: TEnemy;
begin
 en := TEnemy.Create;
 en.List.Add('test');
 ol := TObjectList.Create;
 ol.Add(en);
 ObjectToJsonFile(ol, 'test.json');
 ol.Clear;
 JsonFileToObject('test.json', ol, TEnemy);
 ShowMessage(IntToStr(ol.count));
 ol.Free;

JsonFileToObject fails, although the object list is serialized correctly by ObjectToJsonFile. What I am missing here?
Thanks for all your time.

Last edited by salowabicz (2017-12-03 20:02:03)

Offline

#13 2017-12-03 20:04:23

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: Record property serialization in Delphi < 2010, no type info?

Your field is probably not initialized, since the TEnemy.Create constructor is not created.
Inherit from TSynPersistent, and override the virtual constructor.

As I wrote

Adding a ref-counted field in the record is not a good idea, since it will slow down everything.
And the root cause of the problem is that a record published property has no RTTI in the property field of the class, anyway. So adding a ref-counted field won't be enough.

Offline

#14 2017-12-03 20:24:20

salowabicz
Member
Registered: 2017-12-02
Posts: 6

Re: Record property serialization in Delphi < 2010, no type info?

That did the job. Thank you very much smile

Offline

Board footer

Powered by FluxBB