Blog

All Blog Posts  |  Next Post  |  Previous Post

FNC Hidden Gems: JSON persistence

Tuesday, April 28, 2020

Intro

FNC has evolved over time. Each update brings new features, some of them are visible at designtime/runtime. Some of time are happening behind the screens. Some updates are bringing a lot of fixes to make FNC more stable across the multiple frameworks and platforms FNC supports. JSON persistence is one of those hidden gems that is available in TMS FNC Core. Today's blog brings a step by step tutorial on how to implement JSON persistence in your application, and how to import objects from JSON.

Getting Started

JSON formatted data can come from a REST service, a database, a local file, plain text ... . It's an easy to read/learn/use and consume format and in FNC we have added support for mapping objects to JSON and vice versa. The code below is a sample of JSON that we will map on an object in Delphi. We'll focus on having an object that can access the properties shown in the JSON below. Note that only the published properties can be accessed.

{
   "$type":"TPerson",
   "address":{
      "$type":"TPersonAddress",
      "addressLocality":"Colorado Springs",
      "addressRegion":"CO",
      "postalCode":"80840",
      "streetAddress":"100 Main Street"
   },
   "colleague":[
      "http://www.example.com/JohnColleague.html",
      "http://www.example.com/JameColleague.html"
   ],
   "email":"info@example.com",
   "jobTitle":"Research Assistant",
   "name":"Jane Doe",
   "birthDate":"1979-10-12",
   "gender":"female",
   "nationality":"Albanian",
   "telephone":"(123) 456-6789",
   "url":"http://www.example.com",
}

For accessing this data we need to define our classes first:
type
  TPersonAddress = class(TPersistent)
  private
    FPostalCode: string;
    FAddressLocality: string;
    FAddressRegion: string;
    FStreetAddress: string;
  published
    property AddressLocality: string read FAddressLocality write FAddressLocality;
    property AddressRegion: string read FAddressRegion write FAddressRegion;
    property PostalCode: string read FPostalCode write FPostalCode;
    property StreetAddress: string read FStreetAddress write FStreetAddress;
  end;

  TPerson = class(TPersistent)
  private
    FAddress: TPersonAddress;
    FColleague: TStringList;
    FBirthDate: string;
    FName: string;
    FEmail: string;
    FTelephone: string;
    FGender: string;
    FNationality: string;
    FJobTitle: string;
    FURL: string;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property Address: TPersonAddress read FAddress;
    property Colleague: TStringList read FColleague;
    property Email: string read FEmail write FEmail;
    property JobTitle: string read FJobTitle write FJobTitle;
    property Name: string read FName write FName;
    property BirthDate: string read FBirthDate write FBirthDate;
    property Gender: string read FGender write FGender;
    property Nationality: string read FNationality write FNationality;
    property Telephone: string read FTelephone write FTelephone;
    property URL: string read FURL write FURL;
  end;
And the JSON data.
const
  jsonSample =
    '{' +
      '"$type": "TPerson",' +
      '"address":{' +
        '"$type": "TPersonAddress",' +
        '"addressLocality":"Colorado Springs",' +
        '"addressRegion":"CO",' +
        '"postalCode":"80840",' +
        '"streetAddress":"100 Main Street"' +
      '},' +
      '"colleague":[' +
        '"http://www.example.com/JohnColleague.html",' +
        '"http://www.example.com/JameColleague.html"' +
      '],' +
      '"email":"info@example.com",' +
      '"jobTitle":"Research Assistant",' +
      '"name":"Jane Doe",' +
      '"birthDate":"1979-10-12",' +
      '"gender":"female",' +
      '"nationality":"Albanian",' +
      '"telephone":"(123) 456-6789",' +
      '"url":"http://www.example.com"' +
    '}';
There are multiple ways to map the JSON onto the TPerson object.

1. Use the TTMSFNCPersistence class
Add the unit *TMSFNCPersistence to the uses list (* = FMX., LCL, VCL., WEBLib.), and use the following code:
var
  p: TPerson;
  s: TStringStream;
begin
  p := TPerson.Create;
  s := TStringStream.Create(jsonSample);
  try
    TTMSFNCPersistence.LoadSettingsFromStream(p, s);
  finally
    s.Free;
    p.Free;
  end;
end;
2. Use the object class helper in *TMSFNCTypes unit
An alternative is to use the class helper that maps JSON to the object.
var
  p: TPerson;
begin
  p := TPerson.Create;
  try
    p.JSON := jsonSample;
  finally
    p.Free;
  end;
end;
Writing to JSON is as easy as reading. Simply use SaveSettingsToFile or SaveSettingsToStream or use the JSON object class helper to get the JSON from the object.
  p: TPerson;
begin
  p := TPerson.Create;
  try
    p.JSON := jsonSample;
    p.Name := 'tmssoftware.com';
    TTMSFNCUtils.Log(p.JSON);
    //or
    TTMSFNCPersistence.SaveSettingsToFile(p, 'TPerson.json');
  finally
    p.Free;
  end;
end;
Note that this code will also work on other frameworks such as VCL, LCL and WEB. Below is the full code sample that demonstrates JSON persistence. Please let us know if you have suggestions on what to add to TMS FNC Core in the future!
unit Unit1;

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;

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

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  FMX.TMSFNCTypes, FMX.TMSFNCPersistence;

const
  jsonSample =
    '{' +
      '"$type": "TPerson",' +
      '"address":{' +
        '"$type": "TPersonAddress",' +
        '"addressLocality":"Colorado Springs",' +
        '"addressRegion":"CO",' +
        '"postalCode":"80840",' +
        '"streetAddress":"100 Main Street"' +
      '},' +
      '"colleague":[' +
        '"http://www.example.com/JohnColleague.html",' +
        '"http://www.example.com/JameColleague.html"' +
      '],' +
      '"email":"info@example.com",' +
      '"jobTitle":"Research Assistant",' +
      '"name":"Jane Doe",' +
      '"birthDate":"1979-10-12",' +
      '"gender":"female",' +
      '"nationality":"Albanian",' +
      '"telephone":"(123) 456-6789",' +
      '"url":"http://www.example.com"' +
    '}';

type
  TPersonAddress = class(TPersistent)
  private
    FPostalCode: string;
    FAddressLocality: string;
    FAddressRegion: string;
    FStreetAddress: string;
  published
    property AddressLocality: string read FAddressLocality write FAddressLocality;
    property AddressRegion: string read FAddressRegion write FAddressRegion;
    property PostalCode: string read FPostalCode write FPostalCode;
    property StreetAddress: string read FStreetAddress write FStreetAddress;
  end;

  TPerson = class(TPersistent)
  private
    FAddress: TPersonAddress;
    FColleague: TStringList;
    FBirthDate: string;
    FName: string;
    FEmail: string;
    FTelephone: string;
    FGender: string;
    FNationality: string;
    FJobTitle: string;
    FURL: string;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property Address: TPersonAddress read FAddress;
    property Colleague: TStringList read FColleague;
    property Email: string read FEmail write FEmail;
    property JobTitle: string read FJobTitle write FJobTitle;
    property Name: string read FName write FName;
    property BirthDate: string read FBirthDate write FBirthDate;
    property Gender: string read FGender write FGender;
    property Nationality: string read FNationality write FNationality;
    property Telephone: string read FTelephone write FTelephone;
    property URL: string read FURL write FURL;
  end;

{ TPerson }

constructor TPerson.Create;
begin
  FAddress := TPersonAddress.Create;
  FColleague := TStringList.Create;
end;

destructor TPerson.Destroy;
begin
  FreeAndNil(FAddress);
  FreeAndNil(FColleague);
  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  p: TPerson;
begin
  p := TPerson.Create;
  try
    p.JSON := jsonSample;
  finally
    p.Free;
  end;
end;

end.


Pieter Scheldeman




This blog post has received 7 comments.


1. Tuesday, April 28, 2020 at 7:16:19 PM

very nice!

Price Rhett


2. Wednesday, May 6, 2020 at 5:42:57 PM

Hi Bruno, how''s it go?

Any job at Delphi you have for me? (On-site or remote.)

Al Gonzalez
@algonzalez74 in Twitter

Al Gonzalez


3. Thursday, May 7, 2020 at 3:31:34 PM

Thanks for your interest! We look forward to receive via email your CV & motivation.

Bruno Fierens


4. Saturday, May 16, 2020 at 6:55:22 AM

Thank you very much, Bruno. I already sent you the email with my CV. I await your assessment and response, please.

Best regards.

Al Gonzalez


5. Thursday, June 24, 2021 at 9:00:20 AM

Tried to put this together with the current version and the JSON property seems to be gone. :(


Farmer Christian


6. Thursday, June 24, 2021 at 9:05:10 AM

Hi, I have copy pasted the above sampe in a new project and can still compile and run the example, so maybe you forgot to include a unit? JSON property is included in a class helper in the FMX.TMSFNCTypes unit

Pieter Scheldeman


7. Thursday, June 24, 2021 at 9:16:15 AM

My bad. I setup the uses clause incorrectly.

Farmer Christian




Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post