JSON Persistence
TMS FNC Core can save object state to JSON and load it back into object instances. Use this for component settings, application state, and plain persistent objects that expose the values you want to store through published properties.
The persistence layer is shared by FNC products and is available through framework-prefixed units such as FMX.TMSFNCPersistence, VCL.TMSFNCPersistence, and WEBLib.TMSFNCPersistence.
Required Units
Use the persistence unit when you want direct load and save methods:
uses
FMX.TMSFNCPersistence;
Use the types unit when you want object helper methods such as JSON, ToJSON, Log, SaveToJSONFile, and LoadFromJSONFile:
uses
FMX.TMSFNCTypes;
Persisted Properties
JSON persistence reads and writes published properties. Move values that must be persisted into the published section of your class, the same way Delphi form streaming requires published component properties.
Nested object properties are supported when the nested object can be reached from a published property. String lists and supported generic lists are written as JSON arrays.
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;
FColleagues: TStringList;
FEmail: string;
FName: string;
public
constructor Create;
destructor Destroy; override;
published
property Address: TPersonAddress read FAddress;
property Colleagues: TStringList read FColleagues;
property Email: string read FEmail write FEmail;
property Name: string read FName write FName;
end;
Load From JSON
Use TTMSFNCPersistence.LoadSettingsFromStream when the JSON is already available as a stream.
var
Person: TPerson;
Stream: TStringStream;
begin
Person := TPerson.Create;
Stream := TStringStream.Create(JsonSample);
try
TTMSFNCPersistence.LoadSettingsFromStream(Person, Stream);
finally
Stream.Free;
Person.Free;
end;
end;
Use TTMSFNCObjectPersistence.LoadObjectFromString when the JSON is available as a string.
var
Person: TPerson;
begin
Person := TPerson.Create;
try
TTMSFNCObjectPersistence.LoadObjectFromString(Person, JsonSample);
finally
Person.Free;
end;
end;
When FMX.TMSFNCTypes is in the uses list, you can also assign the JSON string through the object helper:
var
Person: TPerson;
begin
Person := TPerson.Create;
try
Person.JSON := JsonSample;
finally
Person.Free;
end;
end;
Save To JSON
Use TTMSFNCPersistence.SaveSettingsToFile to write object state directly to a JSON file.
var
Person: TPerson;
begin
Person := TPerson.Create;
try
Person.JSON := JsonSample;
Person.Name := 'Joe Heart';
TTMSFNCPersistence.SaveSettingsToFile(Person, 'TPerson.json');
finally
Person.Free;
end;
end;
Use TTMSFNCObjectPersistence.SaveObjectToString when you need the generated JSON as a string.
var
Person: TPerson;
Json: string;
begin
Person := TPerson.Create;
try
Json := TTMSFNCObjectPersistence.SaveObjectToString(Person);
finally
Person.Free;
end;
end;
The object helper also exposes ToJSON:
var
Person: TPerson;
Json: string;
begin
Person := TPerson.Create;
try
Json := Person.ToJSON;
finally
Person.Free;
end;
end;
Class Type Metadata
Saved JSON can include a $type property. The persistence layer uses this class name when it needs to create object instances while loading JSON.
Register classes that may be created from $type metadata:
initialization
RegisterClass(TPerson);
RegisterClass(TPersonAddress);
Class registration is especially important when JSON contains nested objects, collection items, or generic list items that must be instantiated while loading.
Collections
Extend the TPerson example with a TCollection-based relations list:
TPersonRelation = class(TCollectionItem)
private
FName: string;
FDescription: string;
published
property Name: string read FName write FName;
property Description: string read FDescription write FDescription;
end;
TPersonRelations = class(TCollection)
public
constructor Create;
function Add: TPersonRelation;
property Items[Index: Integer]: TPersonRelation read GetItem write SetItem; default;
end;
Add Relations: TPersonRelations as a published property of TPerson. The collection is serialised as a JSON array of objects, each with a "$type" property:
"Relations": [
{ "$type": "TPersonRelation", "Description": "Brother", "Name": "John Doe" },
{ "$type": "TPersonRelation", "Description": "Mother", "Name": "Mia Reyes" }
]
Loading Without $type
When the source JSON has no "$type" keys on array items, implement ITMSFNCBaseListIO on the collection so the persistence layer knows which item class to create:
TPersonRelations = class(TCollection, ITMSFNCBaseListIO)
function GetItemClass: TClass;
// IInterface boilerplate: QueryInterface, _AddRef, _Release
end;
function TPersonRelations.GetItemClass: TClass;
begin
Result := TPersonRelation;
end;
Also implement ITMSFNCBasePersistenceIO on the root object to control item construction:
TPerson = class(TInterfacedPersistent, ITMSFNCBasePersistenceIO)
protected
function CreateObject(const AClassName: string; const ABaseClass: TClass): TObject;
end;
function TPerson.CreateObject(const AClassName: string; const ABaseClass: TClass): TObject;
begin
Result := nil;
if AClassName = 'TPersonRelation' then
Result := TPersonRelation.Create(Relations);
end;
Register the item class so the persistence layer can instantiate it:
initialization
RegisterClass(TPersonRelation);
Loading With $type
When the JSON includes "$type" on every object, use TTMSFNCObjectPersistence.LoadObjectFromString instead of the class helper — the class helper ignores "$type", so collection items will not be created without the interfaces above.
Generics
Replace TPersonRelations = class(TCollection) with a generic object list:
TPersonRelation = class(TPersistent)
// same published properties as before
procedure Assign(Source: TPersistent); override;
end;
TPersonRelations = TObjectList<TPersonRelation>;
The loading and saving mechanism is identical to TCollection. The same ITMSFNCBaseListIO / ITMSFNCBasePersistenceIO interfaces apply when the JSON omits "$type".
TDictionary Support
TObjectDictionary<string, T> is supported. Keys must be strings. Each entry is serialised as a single-key JSON object inside an array:
[
{ "1": { "$type": "TMyObject", "MyProperty": "Value 1" } },
{ "2": { "$type": "TMyObject", "MyProperty": "Value 2" } }
]
Supported Generic Types
| Type | Notes |
|---|---|
TObjectList<TObject> / TList<TObject> |
Items must be TPersistent descendants |
TList<string> |
Serialised as a JSON string array |
TList<Integer> |
Serialised as a JSON integer array |
TList<Double> |
Serialised as a JSON number array |
Undo / Redo Manager
TTMSFNCUndoManager keeps a stack of JSON snapshots for any persistent object. Add TMSFNCUndo to the uses list:
uses
FMX.TMSFNCUndo;
Create one manager per object — each instance manages exactly one object:
var
p: TPerson;
u: TTMSFNCUndoManager;
begin
p := TPerson.Create;
u := TTMSFNCUndoManager.Create(p);
try
TTMSFNCObjectPersistence.LoadObjectFromString(p, jsonSample);
u.PushState('init'); // save clean state
p.Name := 'ERROR';
u.PushState('error_data'); // save changed state
u.Undo; // revert to 'init'
u.Redo; // re-apply 'error_data'
finally
u.Free;
p.Free;
end;
end;
TTMSFNCUndoManager API
| Member | Description |
|---|---|
PushState(AActionName) |
Push a JSON snapshot of the managed object onto the history stack |
Undo |
Restore the previous state |
Redo |
Re-apply the next state |
CanUndo |
True if an undo step is available |
CanRedo |
True if a redo step is available |
NextUndoAction |
Name of the action that would be undone |
NextRedoAction |
Name of the action that would be redone |
ClearUndoStack |
Discard all history |
MaxStackCount |
Maximum entries kept (default: 20) |
Related API
TTMSFNCPersistenceTTMSFNCPersistence.LoadSettingsFromStreamTTMSFNCPersistence.SaveSettingsToFileTTMSFNCObjectPersistenceTTMSFNCObjectPersistence.LoadObjectFromStringTTMSFNCObjectPersistence.SaveObjectToString