r3code
r3code

Reputation: 51

Delphi: superobject serialization custom converter/inverter

I'd like to serialize objects and records to send them and restore on a remote endpoint. There is the shared objects declaration unit for a local and remote end.

I has a record with a field containing different records as JSON Object:

TPmMessage = record
  CorrelationId: TGUID;
  BatchId: TGUID;
  MessageName: string; 
  Data: ISuperObject; // TAlarms, TCommand record and etc. can be inside as JSON object
end;

CASE 1

I need to write a Type name (TRttiType.QualifiedName) in a JSON object for the Data field for deserialization on other side.

An example structure:

  TDeviceInfo = record
    DeviceType: string;
    DeviceIp: string;
  end;

  TAlarmLocation = record
    Name: string;
    rack: Word;
    slot: Word;
    port: Word;
  end;

  TAlarmInfo = record
    AlarmType: string;
    Severity: string;
    ConditionType: string;
    Datetime: TDateTime;
    Location: TAlarmLocation;
  end;

  TAlarm = record
    DeviceInfo: TDeviceInfo;
    Alarm: TAlarmInfo;
  end;

  TAlarmsList = array of TAlarmInfo;

Usage:

var
  msg: TPmMessage;
  als: TAlarms;
  ctx: TSuperRttiContext;
  JsonText: string;
begin
  ctx := TSuperRttiContext.Create;
  try
  SetLength(als,2);
  // init array of als
  ...
  // init TPmMessage Fields
  ...
  // serialize TAlarms
  msg.Data := ctx.AsJson<TAlarms>(als);
  // serialize TPmMessage
  JsonText := ctx.AsJson<TPmMessage>(msg).AsString;
  ...
  // Restore record from JSON object
  msg := ctx.AsType<TPmMessage>(SO(JsonText));
  ...
  finally
    ctx.Free;
  end;
end;

After restore I get an TPmMessage, but I don't know what type in the Data JSON object.

CASE 2

In case 1 I don't know the type of the Data JSON object. So I added the DataObjectType field with QualifiedName as value.

In that case I make another structure for seralization:

  TPmMessageData = record
    DataObjectType: string;
    DataObject: ISuperObject;
  end;

  TPmMessage = record
    Source: string;
    CorrelationId: TGUID;
    BatchId: TGUID;
    MessageName: string;
    Data: TPmMessageData;
  end;

This structure serialized correctly with code:

var 
  msg: TPmMessage;
  ti: PTypeInfo;
  uals: TAlarms;
begin
  {fill Alarms array}
  ti := TypeInfo(TAlarms);
  msg.Data.DataObjectType := ctx.Context.GetType(ti).QualifiedName;
  msg.Data.DataObject := ctx.AsJson<TAlarms>(uals);
end;

DataObject: ISuperObject also serialised well.

A question is How to deserialize the DataObject?

I thought that it's possioble to use

{var DataType: TRttiType;}
DataType := ctx.Context.FindType(DecodedMsg.Data.DataObjectType);
uals := ctx.AsJson<DataType>(DecodedMsg.Data.DataObject);

But the Method 'AsType' requires explicit type argument.

So I think to use case to set the correct type is the only way.

How to serialize/deserialise by using superobject beter?

Could you suggest me a better structure for the messages instead of this?

Upvotes: 3

Views: 3529

Answers (1)

mjn
mjn

Reputation: 36634

You could wrap the JSON serialized object in a container object which has two properties:

  • type name
  • the wrapped object

then in the deserialization, first read the type of the contained object and then load the serialized object into a Delphi object variable of the corresponding type.

However, a interface pointer in a record is not easy to serialize anyway (it contains a pointer instead of a full JSON encoded string) - so I am not sure if the record serialization fails for a different reason.

Upvotes: 1

Related Questions