TMSFNCPersistence how to store a property of TBytes

I would like to use TMS FNC persistence for my project, but I don't know how to store a property with type TBytes (for a BLOB) to a json file. How am I able to achieve this?

TBytes cannot be stored as-is. The types supported in JSON are object, array, number, string, Boolean (value true or false ), or null (value null ) . Can you provide some insight on the class, are you able to manipulate or add properties? If so, you could add a published property BytesAsBase64: string which uses TTMSFNCUtils.Encode64 & TTMSFNCUtils.Decode64 in the getter and setter.

Hi Pieter,

Thanks for the swift reply! Ok, if that is the case that would be the best solution I think. I changed my implementation like you suggested and this works (somewhat like below). I wonder however isn't there a more elegant solution possible when I implement the ITMSFNCCustomReadWriteIO interface and if so how to do that?

I changed my classes, like you suggested:

type
TBaseListItem = class(TInterfacedPersistent)
private
FName: string;
FUniqueId: integer;
FParentUniqueId: integer;
protected
procedure Assign(Source: TBaseListItem); virtual;
public
constructor Create(AParentUniqueId, AUniqueId: integer; const AName: string);
destructor Destroy; override;
published
property ParentUniqueId: integer read FParentUniqueId write FParentUniqueId;
property UniqueId: integer read FUniqueId write FUniqueId;
property Name: string read FName write FName;
end;

// Previous
TPerson = class(TBaseListItem)
private
FDateOfBirth: TDateTime;
FNotes: TBytes;
function GetNotes: TBytes;
procedure SetNotes(const Value: TBytes);
public
constructor Create(AParentUniqueId, AUniqueId: integer; const AName: string;
ANotes: TBytes; ABirthDate: TDateTime);
destructor Destroy; override;
procedure Assign(Source: TPerson);
published
property Notes: TBytes read GetNotes write SetNotes;
property DateOfBirth: TDateTime read FDateOfBirth write FDateOfBirth;
end;

// Changed to
TPerson = class(TBaseListItem)
private
FDateOfBirth: TDateTime;
FNotes: string;
public
constructor Create(AParentUniqueId, AUniqueId: integer; const AName: string;
ANotes: string; ABirthDate: TDateTime);
destructor Destroy; override;
procedure Assign(Source: TPerson);
published
property Notes: string read GetNotes write SetNotes;
property DateOfBirth: TDateTime read FDateOfBirth write FDateOfBirth;
end;

// Main
procedure Main.WriteNotes;
begin
FPerson.Notes := TTMSFNCUtils.Encode64('My notes for this person');
end;

procedure Main.ReadNotes;
var
LNotes: string;
begin
LNotes := TTMSFNCUtils.Decode64(FPerson.Notes);
end;

// JSON Output
{
"$type": "TPerson",
"DateOfBirth": 45614,
"Name": "New Item [2]",
"Notes": "VFBGMAtUU3RhdGVTYXZlcgASU2F2ZVN0YXRlLlByb2R1Y2VyBwRwVkNMElNhdmVTdGF0ZS5Gb250TmFtZQYGVGFob21hElNhdmVTdGF0ZS5Gb250U2l6ZQIIE1NhdmVTdGF0ZS5Gb250U3R5bGULABRTYXZlU3RhdGUuVGV4dENvbG9yQwIAEFNhdmVTdGF0ZS5Db2xvckMEw7/Dv8O/AA9TYXZlU3RhdGUuQ2FyZXQCARRTYXZlU3RhdGUuQ2FyZXRJbmRleAIRF1NhdmVTdGF0ZS5TZWxlY3Rpb25Gcm9tAgEcU2F2ZVN0YXRlLlNlbGVjdGlvbkZyb21JbmRleAIRFVNhdmVTdGF0ZS5TZWxlY3Rpb25UbwIBGlNhdmVTdGF0ZS5TZWxlY3Rpb25Ub0luZGV4AhERU2F2ZVN0YXRlLlZlcnNpb24CAQ1TYXZlU3RhdGUuVG9wAgAOU2F2ZVN0YXRlLkxlZnQCAAAAVFBGMBJURWxlbWVudENsYXNzU2F2ZXIADEVsZW1lbnRDbGFzcwYSVFRhYlBlcnNpc3RFbGVtZW50AABUUEYwDVRFbGVtZW50U2F2ZXIAFVNhdmVFbGVtZW50LkFsaWdubWVudAcNdGFMZWZ0SnVzdGlmeRRTYXZlRWxlbWVudC5CYXNlbGluZQcJdGJSZWd1bGFyGFNhdmVFbGVtZW50LkJ1bGxldEluZGVudAIAElNhdmVFbGVtZW50LkNvbG9yQwTDv8O/w78AFlNhdmVFbGVtZW50LlRleHRDb2xvckMCABRTYXZlRWxlbWVudC5Gb250TmFtZQYIU2Vnb2UgVUkUU2F2ZUVsZW1lbnQuRm9udFNpemUCCRVTYXZlRWxlbWVudC5Gb250U3R5bGULABVTYXZlRWxlbWVudC5IaWdobGlnaHQIElNhdmVFbGVtZW50LkluZGVudAIAD1NhdmVFbGVtZW50LlRhZwIAAABUUEYwElRFbGVtZW50Q2xhc3NTYXZlcgAMRWxlbWVudENsYXNzBgxUVGV4dEVsZW1lbnQAAFRQRjANVEVsZW1lbnRTYXZlcgAVU2F2ZUVsZW1lbnQuQWxpZ25tZW50Bw10YUxlZnRKdXN0aWZ5FFNhdmVFbGVtZW50LkJhc2VsaW5lBwl0YlJlZ3VsYXIYU2F2ZUVsZW1lbnQuQnVsbGV0SW5kZW50AgASU2F2ZUVsZW1lbnQuQ29sb3JDBMO/w7/DvwAWU2F2ZUVsZW1lbnQuVGV4dENvbG9yQwIAFFNhdmVFbGVtZW50LkZvbnROYW1lBgZUYWhvbWEUU2F2ZUVsZW1lbnQuRm9udFNpemUCCBVTYXZlRWxlbWVudC5Gb250U3R5bGULABVTYXZlRWxlbWVudC5IaWdobGlnaHQIEFNhdmVFbGVtZW50LlRleHQGDUhlbGxvIFdvcmxkISASU2F2ZUVsZW1lbnQuSW5kZW50AgAPU2F2ZUVsZW1lbnQuVGFnAgARU2F2ZUVsZW1lbnQuRXJyb3IIFFNhdmVFbGVtZW50LlN0eWxlVGFnAgAQU2F2ZUVsZW1lbnQuTGluZQgAAFRQRjASVEVsZW1lbnRDbGFzc1NhdmVyAAxFbGVtZW50Q2xhc3MGDFRUZXh0RWxlbWVudAAAVFBGMA1URWxlbWVudFNhdmVyABVTYXZlRWxlbWVudC5BbGlnbm1lbnQHDXRhTGVmdEp1c3RpZnkUU2F2ZUVsZW1lbnQuQmFzZWxpbmUHCXRiUmVndWxhchhTYXZlRWxlbWVudC5CdWxsZXRJbmRlbnQCABJTYXZlRWxlbWVudC5Db2xvckMEw7/Dv8O/ABZTYXZlRWxlbWVudC5UZXh0Q29sb3JDBAAAw78AFFNhdmVFbGVtZW50LkZvbnROYW1lBgZUYWhvbWEUU2F2ZUVsZW1lbnQuRm9udFNpemUCCBVTYXZlRWxlbWVudC5Gb250U3R5bGULABVTYXZlRWxlbWVudC5IaWdobGlnaHQIEFNhdmVFbGVtZW50LlRleHQGEVRoaXMgaXMgcmVkIHRleHQhElNhdmVFbGVtZW50LkluZGVudAIAD1NhdmVFbGVtZW50LlRhZwIAEVNhdmVFbGVtZW50LkVycm9yCBRTYXZlRWxlbWVudC5TdHlsZVRhZwIAEFNhdmVFbGVtZW50LkxpbmUIAAA=",
"ParentUniqueId": 0,
"UniqueId": 2
}

You can indeed custom read/write properties that are not available by default on your class.

unit Unit9;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.TMSFNCPersistence, FMX.TMSFNCJSONReader, FMX.TMSFNCJSONWriter;

type
  TForm9 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TPerson = class(TInterfacedPersistent, ITMSFNCCustomReadWriteIO)
  private
    FNotes: TBytes;
    FX: Integer;
    FY: string;
  protected
    procedure CustomWriteProperty(AObject: TObject; APropertyName: string; APropertyKind: TTypeKind; AWriter: TTMSFNCJSONWriter; var ACanWrite: Boolean);
    procedure CustomReadProperty(AObject: TObject; APropertyName: string; APropertyKind: TTypeKind; AReader: TTMSFNCJSONReader; var ACanRead: Boolean);
  published
    property Notes: TBytes read FNotes write FNotes;
    property X: Integer read FX write FX;
    property Y: string read FY write FY;
  end;

var
  Form9: TForm9;

implementation

{$R *.fmx}

uses
  FMX.TMSFNCUtils, FMX.TMSFNCTypes;

{ TPerson }

procedure TPerson.CustomReadProperty(AObject: TObject; APropertyName: string;
  APropertyKind: TTypeKind; AReader: TTMSFNCJSONReader; var ACanRead: Boolean);
var
  s: string;
begin
  if APropertyName = 'Notes' then
  begin
    ACanRead := False;
    s := AReader.ReadString;
    Notes := TTMSFNCUtils.Decode64ToBytes(s);
  end;
end;

procedure TPerson.CustomWriteProperty(AObject: TObject; APropertyName: string;
  APropertyKind: TTypeKind; AWriter: TTMSFNCJSONWriter; var ACanWrite: Boolean);
begin
  if APropertyName = 'Notes' then
  begin
    ACanWrite := False;
    AWriter.WriteName(APropertyName);
    AWriter.WriteString(TTMSFNCUtils.Encode64(Notes));
  end;
end;

procedure TForm9.FormCreate(Sender: TObject);
var
  t: TPerson;
  s: string;
begin
  t := TPerson.Create;
  t.Notes := [0, 1, 2, 3, 4, 5, 6];
  t.X := 20;
  t.Y := 'Hello';
  t.Log;
  s := t.JSON;
  t.JSON := s;
  t.free;
end;

end.

Thanks Pieter! This is exactly what I was looking for.

1 Like