JSON

J.Parse('{"user":{"name":"Olivier"}}');
Trace(J.GetString('user.name'));

TJson

TJson is a structured-data container that holds an object, array, or scalar value. The API is built around path strings that navigate nested objects and arrays in a single expression.

Path syntax

Path Meaning
"user.name" The name field inside the user object.
"items[2].id" The id field of the third element of the items array.
"matrix[0][3]" The fourth element of the first row of a 2D array.
"" or "." The root document itself.

When a SetXxx call targets a path whose parent objects do not exist yet, the missing intermediate objects are created automatically. Array indices on SetXxx, however, must already exist (use ArrayAddXxx to grow arrays).

Reference

Procedure Description
function TJson.Create: TJson; Create an empty JSON document (root is an empty object). Always call Create before use.
procedure TJson.Free; Release the document and all its children. Call in Destroy.
procedure TJson.Clear; Reset the document to an empty object.
function TJson.Parse(s: string): Boolean; Parse s into the document. Returns True on success, False on parse error.
function TJson.AsText: string; Serialise the current document back to a compact JSON string.
procedure TJson.SaveToFile(FileName: string); Save the document to disk as UTF-8 JSON.
function TJson.LoadFromFile(FileName: string): Boolean; Load a JSON file into the document. Returns True on success.
function TJson.Has(path: string): Boolean; Returns True if a value exists at path.
function TJson.Count(path: string): integer; Number of items in an array or members in an object at path.
function TJson.Count: integer; Same, for the root document.
function TJson.IsArray(path: string): Boolean; True if the node at path is an array.
function TJson.IsObject(path: string): Boolean; True if the node at path is an object.
function TJson.GetString(path: string): string; Read a string value at path. Returns '' if absent.
function TJson.GetFloat(path: string): single; Read a numeric value as a float. Returns 0 if absent.
function TJson.GetInt(path: string): integer; Read a numeric value as an integer. Returns 0 if absent.
function TJson.GetBool(path: string): Boolean; Read a boolean value at path. Returns False if absent.
procedure TJson.SetString(path: string; val: string); Set a string at path, creating missing parents.
procedure TJson.SetFloat(path: string; val: single); Set a numeric (float) value at path.
procedure TJson.SetInt(path: string; val: integer); Set a numeric (integer) value at path.
procedure TJson.SetBool(path: string; val: Boolean); Set a boolean value at path.
procedure TJson.SetNull(path: string); Set a JSON null value at path.
procedure TJson.SetObject(path: string); Create an empty object at path.
procedure TJson.SetArray(path: string); Create an empty array at path.
procedure TJson.Remove(path: string); Delete the field or array element at path.
procedure TJson.ArrayAddString(path: string; val: string); Append a string element to the array at path.
procedure TJson.ArrayAddFloat(path: string; val: single); Append a float element to the array at path.
procedure TJson.ArrayAddInt(path: string; val: integer); Append an integer element to the array at path.
procedure TJson.ArrayAddBool(path: string; val: Boolean); Append a boolean element to the array at path.
procedure TJson.Keys(path: string; resultList: TStringList); Fill resultList with the keys of the object at path.
////////////////////////////////////////////////////////////////////////////////
// TJson Methods Demo - exercises every method of the JSON API in a single
// Init pass. Open the trace window to follow the output.
////////////////////////////////////////////////////////////////////////////////
procedure Init;
var J  : TJson;
    SL : TStringList;
    i  : integer;
begin
  J.Create;

  // 1. parse / serialise
  J.Parse('{"user":{"name":"Olivier","age":42},"tags":["a","b"]}');
  Writeln('after parse: ', J.AsText);

  // 2. file I/O
  J.SaveToFile('config.json');
  J.LoadFromFile('config.json');

  // 3. read by path
  Writeln('user.name = ', J.GetString('user.name'));   // -> "Olivier"
  Trace  ('user.age  = ', J.GetInt   ('user.age'));    // -> 42
  Writeln('tags[0]   = ', J.GetString('tags[0]'));     // -> "a"
  Trace  ('tags count = ', J.Count('tags'));           // -> 2

  if J.Has('user.email')
  then Writeln('user.email exists')
  else Writeln('user.email is missing');

  // 4. write by path (missing intermediates are created)
  J.SetString('user.email', 'olivier@bespline.com');
  J.SetFloat ('audio.gain', 0.75);
  J.SetBool  ('flags.muted', true);
  J.SetObject('newSection');
  J.SetArray ('items');

  // 5. append into arrays
  J.ArrayAddString('tags',  'c');
  J.ArrayAddFloat ('items', 1.5);

  // 6. remove
  J.Remove('user.age');
  J.Remove('tags[0]');

  // 7. enumerate the keys of an object
  SL.Create;
  J.Keys('user', SL);
  Writeln('keys of "user":');
  for i := 0 to SL.count - 1 do
    Writeln('  - ', SL.getstring(i));
  SL.Free;

  // 8. final state
  Writeln('final: ', J.AsText);

  J.Free;
end;

TJson.Create

function TJson.Create: TJson;

Initializes the document. Always call Create before use.

J.Create;

TJson.Free

procedure TJson.Free;

Releases the document and all its children. Call in Destroy.

procedure Destroy;
begin
  J.Free;
end;

TJson.Clear

procedure TJson.Clear;

Resets the document to an empty object, freeing all current content.

J.Clear;

TJson.Parse

function TJson.Parse(s: string): Boolean;

Parses a JSON literal into the document. Returns True on success. On failure the existing document is preserved and an error is logged to the trace.

if J.Parse('{"tempo": 120, "swing": 0.5}')
then Trace('parsed ok')
else Trace('invalid JSON');

TJson.AsText

function TJson.AsText: string;

Returns a compact JSON representation of the current document. Useful for piping into a text outlet or saving manually.

jsonOut.asString(J.AsText);

TJson.SaveToFile

procedure TJson.SaveToFile(FileName: string);

Writes the document to disk in UTF-8. Missing parent directories are created automatically.

J.SaveToFile('Scripts/state.json');

TJson.LoadFromFile

function TJson.LoadFromFile(FileName: string): Boolean;

Loads and parses a JSON file. Returns True on success. The previous document content is replaced only when the load succeeds.

if not J.LoadFromFile('Scripts/state.json')
then Trace('could not load state');

TJson.Has

function TJson.Has(path: string): Boolean;

Tests whether the path exists in the document. Useful to guard reads on optional fields.

if J.Has('audio.gain')
then gain.asFloat(J.GetFloat('audio.gain'));

TJson.Count

function TJson.Count(path: string): integer; function TJson.Count: integer;

Returns the number of items in an array or members in an object at path. The parameterless overload targets the root document.

for i := 0 to J.Count('voices') - 1 do
  Trace(J.GetString('voices[' + IntToStr(i) + '].name'));

TJson.IsArray / TJson.IsObject

function TJson.IsArray(path: string): Boolean;
function TJson.IsObject(path: string): Boolean;

Type predicates for the node at path. Use them before iterating or before navigating deeper into a structure of mixed types.

if J.IsArray('tags') then
  for i := 0 to J.Count('tags') - 1 do
    Trace(J.GetString('tags[' + IntToStr(i) + ']'));

TJson.GetString / GetFloat / GetInt / GetBool

function TJson.GetString(path: string): string;
function TJson.GetFloat (path: string): single;
function TJson.GetInt   (path: string): integer;
function TJson.GetBool  (path: string): Boolean;

Read a scalar at path. Missing or wrongly typed values return a safe default ('', 0, 0, False). Use Has first when the absence of a value is meaningful.

userName := J.GetString('user.name');
gain     := J.GetFloat ('audio.gain');
voices   := J.GetInt   ('synth.maxVoices');
muted    := J.GetBool  ('flags.muted');

TJson.SetString / SetFloat / SetInt / SetBool

procedure TJson.SetString(path: string; val: string);
procedure TJson.SetFloat (path: string; val: single);
procedure TJson.SetInt   (path: string; val: integer);
procedure TJson.SetBool  (path: string; val: Boolean);

Write a scalar at path. Missing intermediate objects are created automatically, so a single call can deep-populate a fresh document.

J.SetString('user.name',    'Olivier');
J.SetFloat ('audio.gain',   0.75);
J.SetInt   ('synth.voices', 16);
J.SetBool  ('flags.muted',  true);

TJson.SetNull / SetObject / SetArray

procedure TJson.SetNull  (path: string);
procedure TJson.SetObject(path: string);
procedure TJson.SetArray (path: string);

Write a JSON null, an empty object, or an empty array at path. After SetArray you can populate the array with ArrayAddXxx.

J.SetObject('audio');           // create empty container
J.SetArray ('audio.channels');  // create empty list inside it
J.SetNull  ('audio.notes');     // explicit null marker

TJson.Remove

procedure TJson.Remove(path: string);

Deletes the field or array element at path. Silently does nothing when the path does not exist.

J.Remove('user.age');     // drop an object field
J.Remove('tags[0]');      // drop the first array element

TJson.ArrayAddString / ArrayAddFloat / ArrayAddInt / ArrayAddBool

procedure TJson.ArrayAddString(path: string; val: string);
procedure TJson.ArrayAddFloat (path: string; val: single);
procedure TJson.ArrayAddInt   (path: string; val: integer);
procedure TJson.ArrayAddBool  (path: string; val: Boolean);

Append an element to the array at path. The array must already exist (create it first with SetArray).

J.SetArray('tags');
J.ArrayAddString('tags', 'kick');
J.ArrayAddString('tags', 'snare');
J.ArrayAddInt   ('tags', 808);

TJson.Keys

procedure TJson.Keys(path: string; resultList: TStringList);

Fills resultList with the keys of the object at path. The list is cleared first. Combine with Count and GetXxx to iterate dynamic objects.

var SL : TStringList;
    i  : integer;
begin
  SL.Create;
  J.Keys('user', SL);
  for i := 0 to SL.count - 1 do
    Trace(SL.getstring(i), ' = ', J.GetString('user.' + SL.getstring(i)));
  SL.Free;
end;

Tips and gotchas

  • Lifecycle is manual. Always pair Create with Free, exactly like TStringList. The script engine does not garbage-collect.
  • SetXxx creates intermediates but ArrayAddXxx does not. Call SetArray('path.to.list') before appending elements if the array might be missing.
  • Path strings are case-sensitive. user.Name and user.name are different keys.
  • Failed Parse keeps the previous content. That makes it safe to call from a Callback watching a live text inlet — bad partial input will not wipe a valid document.
  • Not thread-safe. If Process and Callback touch the same document, serialise access yourself (e.g. queue messages between them) — the same caution applies to TStringList.
  • Numbers are stored as 64-bit doubles internally. Reading GetInt truncates; reading GetFloat returns a single cast.

more about scripts

version 7.0.250121

Edit All Pages