Table of Contents

JSON Framework

The TMS BCL JSON framework provides two complementary approaches for working with JSON data:

  • JSON Document Object Model (DOM): A set of classes for building, reading, and manipulating JSON structures in memory. Use the DOM when you need fine-grained control over the JSON structure.
  • JSON Serialization: A high-level API for converting Delphi objects, records, and values to and from JSON automatically. Use serialization when working with typed Delphi data.

JSON Document Object Model

The JSON DOM is a tree of TJElement objects that mirrors the structure of a JSON document. The class hierarchy is:

  • TJElement - Abstract base class for all JSON elements.
  • TJObject - A collection of named members (JSON object {}).
  • TJArray - An ordered list of elements (JSON array []).
  • TJPrimitive - A single value: string, number, or boolean.
  • TJNull - The JSON null value.

All DOM classes are declared in the Bcl.Json.Classes unit.

Building a JSON Object

Use TJObject to create a JSON object and add members with the Add method. The method accepts string, integer, int64, double, boolean, and TJElement values:

  Obj: TJObject;
...
  Obj := TJObject.Create;
  Obj.Add('name', 'John');
  Obj.Add('age', 30);
  Obj.Add('active', True);
  Obj.Add('score', 9.5);

Nested objects are added by creating a separate TJObject and passing it as a value:

  Address: TJObject;
...
  Address := TJObject.Create;
  Address.Add('street', '123 Main St');
  Address.Add('city', 'Springfield');
  Obj.Add('address', Address);

Building a JSON Array

Use TJArray to create an ordered list of elements. Arrays can hold mixed types, including nested objects and other arrays:

  Arr: TJArray;
  Obj: TJObject;
...
  Arr := TJArray.Create;
  Arr.Add('first value');
  Arr.Add(1.5);
  Arr.Add(True);
  Arr.Add(TJNull.Create);

  Obj := TJObject.Create;
  Obj.Add('prop', 'value');

  Arr.Add(Obj);

Reading Values from JSON Elements

Access object members by name using the default Members property. The returned TJElement can be converted to a Delphi value using the AsString, AsInteger, AsDouble, AsBoolean, or AsInt64 methods:

  Name: string;
  Age: Integer;
...
  Name := Obj['name'].AsString;
  Age := Obj['age'].AsInteger;
Warning

Accessing a member that does not exist returns nil. Calling AsString or similar methods on nil raises an access violation. Always check for existence first or use the safe helper functions described below.

Checking Member Existence

Use TJObject.Contains to verify whether a member exists before reading:

  if Obj.Contains('email') then
    Name := Obj['email'].AsString;

Safe Value Access with Helper Functions

The helper functions JStringOrDefault, JIntegerOrDefault, JBooleanOrDefault, and JDoubleOrDefault return a default value when the element is nil:

  Name := JStringOrDefault(Obj['email']); // returns '' if not found
  Age := JIntegerOrDefault(Obj['age']);    // returns 0 if not found

Type Checking and Casting

Every TJElement provides Is* methods to check its runtime type and As* methods to cast it:

  Element: TJElement;
  Obj: TJObject;
  Arr: TJArray;
  Value: string;
...
  if Element.IsObject then
    Obj := Element.AsObject
  else if Element.IsArray then
    Arr := Element.AsArray
  else if Element.IsPrimitive then
  begin
    if Element.IsString then
      Value := Element.AsString;
  end
  else if Element.IsNull then
    // handle null

The As* casting methods raise EInvalidJsonCast if the element is not of the expected type.

Iterating Over Elements

Iterate over array elements using for..in or index-based access:

  Item: TJElement;
  Index: Integer;
...
  Item := Arr[0];
  for Item in Arr do
    // process item
...
  for Index := 0 to Arr.Count - 1 do
    Item := Arr[Index];

Iterate over object members using for..in, which yields TJMember instances with Name and Value properties:

  Member: TJMember;
...
  for Member in Obj do
    // Member.Name is the property name
    // Member.Value is the TJElement value

Removing Members

Remove a member from an object by name using TJObject.Remove. If the member does not exist, no action is taken:

  Obj.Remove('active');

Delete an array element by index using TJArray.Delete.

Memory Management

TJObject owns its members and TJArray owns its elements. When a container is freed, all contained elements are freed automatically. Do not free elements that belong to a container manually.

Parsing and Writing JSON Strings

Use the TJson class to convert between JSON strings and DOM elements. The Deserialize method parses a JSON string into a TJElement tree:

  JsonStr: string;
  Element: TJElement;
  Obj: TJObject;
...
  JsonStr := '{"name":"John","age":30}';
  Element := TJson.Deserialize<TJElement>(JsonStr);
  Obj := Element.AsObject;
  // use the object...

The Serialize method converts any TJElement back to a JSON string:

  Output: string;
...
  Obj := TJObject.Create;
  Obj.Add('name', 'John');
  Obj.Add('age', 30);
  Output := TJson.Serialize(Obj);
  // Output = '{"name":"John","age":30}'

Both methods are declared in the Bcl.Json unit.

JSON Serialization

The serialization system converts Delphi objects, records, lists, and primitive types to and from JSON automatically. The main entry point is the TJson class.

Serializing Objects

Pass any object to TJson.Serialize to produce a JSON string. By default, all published and public fields are serialized using the field name (with the F prefix removed) as the JSON property name:

  TPerson = class
  private
    FName: string;
    FAge: Integer;
  public
    property Name: string read FName write FName;
    property Age: Integer read FAge write FAge;
  end;
  Person: TPerson;
  Json: string;
...
  Person := TPerson.Create;
  Person.Name := 'Alice';
  Person.Age := 25;
  Json := TJson.Serialize(Person);
  // Json = '{"Name":"Alice","Age":25}'

Deserializing Objects

Use TJson.Deserialize with a type parameter to create an object instance from a JSON string:

  Restored: TPerson;
...
  Json := '{"Name":"Bob","Age":32}';
  Restored := TJson.Deserialize<TPerson>(Json);
  // Restored.Name = 'Bob', Restored.Age = 32
Note

The deserializer creates a new object instance. The caller is responsible for freeing it.

Serializing Lists and Arrays

Generic lists (TObjectList<T>, TList<T>) and dynamic arrays (TArray<T>) are serialized as JSON arrays:

  People: TObjectList<TPerson>;
  ListJson: string;
...
  People := TObjectList<TPerson>.Create;
  Person := TPerson.Create;
  Person.Name := 'Alice';
  Person.Age := 25;
  People.Add(Person);
  ListJson := TJson.Serialize(People);
  // ListJson = '[{"Name":"Alice","Age":25}]'

Supported Types

The serializer handles the following types automatically:

  • Primitive types: string, Integer, Int64, Double, Boolean, Currency, Byte, Word, Cardinal, and other numeric types.
  • Date/time types: TDateTime (ISO 8601 format), TDate, TTime.
  • Binary types: TBytes and TArray<Byte> (Base64 encoding).
  • Enumerations and sets: Serialized as strings and string arrays respectively.
  • GUIDs: Serialized as strings without braces.
  • Collections: TObjectList<T>, TList<T>, TStrings, dynamic arrays.
  • Objects: Serialized as JSON objects with their fields as properties.
  • Nullable types: Nullable values serialize as the inner value or null.
  • Assignable types: Assignable values deserializes as not assigned if property is not present in JSON.
  • JSON DOM types: TJElement and its descendants are serialized directly.

Stream Support

Both TJson.Serialize and TJson.Deserialize accept TStream parameters for reading from or writing to streams instead of strings.

Serialization Attributes

Attributes from the Bcl.Json.Attributes unit control how individual fields and types are serialized.

JsonProperty

The JsonProperty​Attribute attribute specifies a custom JSON property name for a field:

  TCustomer = class
  private
    [JsonProperty('customer_name')]
    FName: string;
    FEmail: string;
    [JsonIgnore]
    FInternalId: Integer;
  public
    property Name: string read FName write FName;
    property Email: string read FEmail write FEmail;
    property InternalId: Integer read FInternalId write FInternalId;
  end;
  Customer: TCustomer;
  Json: string;
...
  Customer := TCustomer.Create;
  Customer.Name := 'Jane';
  Customer.Email := 'jane@example.com';
  Customer.InternalId := 42;
  Json := TJson.Serialize(Customer);
  // Json = '{"customer_name":"Jane","Email":"jane@example.com"}'
  // Note: InternalId is excluded by JsonIgnore

JsonIgnore

The JsonIgnoreAttribute attribute excludes a field from both serialization and deserialization, as shown in the example above where FInternalId is marked with [JsonIgnore].

JsonInclude

The JsonIncludeAttribute attribute controls when a property is included in the output. Use TInclusionMode.NonDefault to omit properties that hold their default value (zero, empty string, nil):

  TOptionalData = class
  private
    FRequiredField: string;
    [JsonInclude(TInclusionMode.NonDefault)]
    FOptionalField: string;
  end;

JsonManaged

The JsonManagedAttribute attribute indicates that the lifetime of an object field is managed by the containing class. The deserializer will not destroy these objects after deserialization. Apply it to individual fields or to the class declaration to affect all object fields.

Note

When in doubt, always use JsonManagedAttribute for object fields, because it works fine if your class is designed the "Delphi-way", which is creating and destroying its associated objects.

If you don't use it, the deserializer will keep track of all created objects and will destroy them when the deserializer is destroyed. This can lead to access violations if your class also tries to manage the lifetime of these objects, which is common in Delphi.

  TChildItem = class
  private
    FValue: string;
  end;

  [JsonManaged]
  TParentItem = class
  private
    FChild: TChildItem;
  public
    constructor Create;
    destructor Destroy; override;
  end;

JsonConverter

The JsonConverter​Attribute attribute specifies a custom converter class for a field or type. See the Custom Converters section for details.

Naming Strategies

A naming strategy controls how Delphi field and property names are mapped to JSON property names. Apply a strategy to a class using the JsonNamingStrategy​Attribute attribute.

The following built-in strategies are available in the Bcl.​Json.​Naming​Strategies unit:

Strategy Field FFirstName Property FirstName
TDefaultNaming​Strategy FirstName FirstName
TCamelCaseNaming​Strategy firstName firstName
TSnakeCaseNaming​Strategy first_name first_name
TIdentityNaming​Strategy FFirstName FirstName
TIdentityCamel​Case​Naming​Strategy fFirstName firstName
TIdentitySnake​Case​Naming​Strategy f_first_name first_name

The default, camelCase, and snake_case strategies strip the leading F prefix from field names before applying the transformation. The identity variants preserve the raw member name.

Applying a Naming Strategy

  [JsonNamingStrategy(TCamelCaseNamingStrategy)]
  TProduct = class
  private
    FProductName: string;
    FUnitPrice: Double;
  public
    property ProductName: string read FProductName write FProductName;
    property UnitPrice: Double read FUnitPrice write FUnitPrice;
  end;
  Product: TProduct;
  Json: string;
...
  Product := TProduct.Create;
  Product.ProductName := 'Widget';
  Product.UnitPrice := 9.99;
  Json := TJson.Serialize(Product);
  // Json = '{"productName":"Widget","unitPrice":9.99}'

Custom Converters

For types that require special serialization logic, create a custom converter by descending from TCustomJsonConverter and implementing the WriteJson and ReadJson methods:

  TMyDateConverter = class(TCustomJsonConverter)
  protected
    procedure WriteJson(const Writer: TJsonWriter; const Value: TValue); override;
    procedure ReadJson(const Reader: TJsonReader; var Value: TValue); override;
  end;

Apply the converter to a field using the JsonConverter​Attribute attribute:

  TEvent = class
  private
    FTitle: string;
    [JsonConverter(TMyDateConverter)]
    FEventDate: TDateTime;
  end;

The converter can also be registered globally by adding a factory to the TJsonConverters registry via TJsonSerializer.​Converters or TJsonDeserializer.​Converters.