grijjy / GrijjyFoundation

Foundation classes used by other Grijjy repositories
Other
249 stars 90 forks source link

How to serialize TStringList using TgoBsonSerializer? #31

Closed tstoicescu closed 4 years ago

tstoicescu commented 4 years ago

If I have a property of TStringList or TStrings type I got an exception If I decorated with [BsonIgnore] attribute nothing happen, serialization dont work. If I remove this TStringList property all work as expected.

neslib commented 4 years ago

You cannot serialize TStringList or many other RTL classes. You can only serialize objects that are themselves serializable. That usually means only classes you define yourself for serialization purposes.

If you want to serialize a list of strings, you could add a serializable property that converts the list to a dynamic array. For example:

type
  TFoo = class
  private
    FStrings: TStringList;
    function GetStringArray: TArray<String>;
    procedure SetStringArray(const Value: TArray<String>);
  public
    constructor Create;
    destructor Destroy; override;

    [BsonIgnore]
    property Strings: TStringList read FStrings;

    property StringArray: TArray<String> read GetStringArray write SetStringArray;
  end;

{ TFoo }

constructor TFoo.Create;
begin
  FStrings := TStringList.Create;
end;

destructor TFoo.Destroy;
begin
  FStrings.Free;
  inherited;
end;

function TFoo.GetStringArray: TArray<String>;
begin
  Result := FStrings.ToStringArray;
end;

procedure TFoo.SetStringArray(const Value: TArray<String>);
begin
  FStrings.Clear;
  FStrings.AddStrings(Value);
end;

This skips serialization of the Strings property, but serializes the StringArray property instead. For example:

procedure Text;
var
  Foo: TFoo;
  Json: String;
begin
  Foo := TFoo.Create;
  Foo.Strings.Add('Bar');
  Foo.Strings.Add('Baz');
  TgoBsonSerializer.Serialize(Foo, Json);
  Assert(Json = '{ "StringArray" : ["Bar", "Baz"] }');
  Foo.Free;
  Foo := nil;

  TgoBsonSerializer.Deserialize(Json, Foo);
  Assert(Foo <> nil);
  Assert(Foo.Strings.Count = 2);
  Assert(Foo.Strings[0] = 'Bar');
  Assert(Foo.Strings[1] = 'Baz');
  Foo.Free;
end;

See the section "Using Classes" in the documentation in Grijjy.Bson.Serialization for more information about limitations when using classes. In general, serialization works best with records instead of classes.

Let me know if this addresses your issue.

tstoicescu commented 4 years ago

Thanks,

If I included

[BsonIgnore] property Strings: TStringList read FStrings;

Exception class EAssertionFailed with message 'Assertion failure (D:\wGit\DataLightXE\DataLightServer\fmxDataLightERP\lib\GrijjyFoundation\Grijjy.Bson.Serialization.pas, line 4777

Seems that [BsonIgnore] attribute is ignored.

neslib commented 4 years ago

That probably means that you need to add the Grijjy.Bson.Serialization unit to the uses clause of the unit where you use the attribute. That's because the Delphi compiler needs to know where to find the [BsonIgnore] attribute.

You are probably getting a "Unknown custom attribute" warning now when you compile your project...

tstoicescu commented 4 years ago

Indeed, I forgot to add Grijjy.Bson.Serialization,

Thanks