Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works

Random project

jsonVariant

Easily use JSON in Delphi applications as a custom variant

View on GitHub

Anyone may press Interesting, signed in or not. It helps others find this project.

Member rating

No ratings yet.

Sign in to rate this project.

README

jsonVariant

jsonVariant is custom variant implementation for making working with JSON in Delphi a very easy experience.

Homepage: https://github.com/CosminFr/jsonVariant

👩‍🏫 Usage Instructions

Simply add JsonVariant.pas in your project and start using it.

pascal
uses
  System.Variants, JsonVariant;

Create JSON Variant

To create a JSON variant use one of the VarJSONCreate functions:

pascal
function VarJSONCreate(const aJsonText: String): Variant;     overload;
function VarJSONCreate: Variant;                              overload;
function VarJSONCreate(const Args: array of const): Variant;  overload;
function VarJSONCreate(const aVarArray: Variant): Variant;    overload;

JSON from text/string

pascal
var
  jData : Variant;
begin
  jData := VarJSONCreate(jsonText);

[!WARNING] If jsonText does not hold a valid JSON, an EJSONParseException is raised.

[!TIP] Check the exception message for a hint (line, position) where it failed.

Empty JSON Object

pascal
  jData := VarJSONCreate();

Empty JSON Array

pascal
  jData := VarJSONCreate([]);

JSON Array from another array

pascal
var
  jPhones : Variant;
begin
  jPhones := VarJSONCreate(['0400 123 456', '+614 0012 3333', '(03) 0012 5555']);

or from a variant array vArrPhones: Variant

pascal
  jPhones := VarJSONCreate(vArrPhones);

JSON content

Access JSON properties like you would with a normal Delphi object.

pascal
  json := VarJSONCreate('{"Name": "John Smith",	"Phone": "(03) 1234 5555"}');
  json.Height := "1.79m";
  ShowMessage(Format('%s (%s): %s', [json.Name, json.Height, json.Phone]));

or for an array

pascal
  jArr := VarJSONCreate([1, 2, 4, 8]);
  jArr.Add(16);

JSON output

To get the JSON text from a JSON Variant one can simply cast it to a string:

pascal
  memJSON.Text := json;

or use one of the custom functions [As|To][String|JSON|Text]:

pascal
  memJSON.Text := json.AsString;

[!TIP] The default output provides a "compact" version. To get a "pretty" version, use "AsText" function.

Case sensitivity

Pay attention to the case of JSON Object properties as JSON is case sensitive! Name and name are different properties!

Name conflict

If a property of the JSON object conflicts with a custom VarJSON property or function, the former "wins". However, since JSON object property is case sensitive while Delphi functions are not, a simple solution to avoid the conflict is to use a different case.

For example, a JSON object with property "Count":

pascal
  json:= VarJSONCreate('{"Count": 15}');

json.Count refers to that specified property =15. To access Count as the custom function representing the number of "Name/Value" pairs in the object =1 simply use a different case (aka json.count or json.COUNT, ...).

Date values

There is no specific type in JSON to handle dates. The usual behaviour is to format the date using ISO 8601 rules.

pascal
  json.Date := Now;
  LocalDate := json.Date;           //might raise conversion error if it was not converted to a TDateTime!
  LocalDate := AsDate(json.Date);   //ensures the correct type, but returns 0 if conversion fails!

[!NOTE] For a string property of length 24 (like the Date above) a conversion is attempted using ISO8601 format. If successful, json.Date returns a TDateTime variant, otherwise the string value is returned.

If a custom format is used/required, please use the "AsString/AsDate" functions with a specific format:

pascal
  json.Date := AsString(Now, 'YYYY/MM/DD');
  LocalDate := AsDate(json.Date, 'YYYY/MM/DD');

🧬 Custom properties and functions

For both Object and Array

ToString, AsString
Calls "ToString" on the associated TJSONValue.

ToJson, AsJson
Calls "ToJSON" on the associated TJSONValue and returns a compact JSON text.

ToText, AsText
Calls "Format" on the associated TJSONValue and returns the indented (aka pretty) formatted JSON text.

Items(i), Values(i), Get(i)
Returns the Variant Value on position i. May raise "out of range" errors!

[!NOTE] Also works as indexed property (aka Items[i], Values[i], Get[i])

Items, Values
Returns a variant array with the Values. This creates a copy of all values as variants!

Clear, Empty
As one would expect removes all pairs/values from the Objecy or Array.

IndexOf(str)
Returns the index of the value that matches the str param; -1 if no match found.

[!TIP] For objects, the str is also compared with the key name.
Used to find the position of a specific property in the JSON Object.

FindValue(str)
Returns the variant containing the TJSNOValue as provided by FindValue(str). It uses the System.JSON.TJSONPathParser to evaluate the str param.
Does not handle * or .. "wildcard" patterns.

JSONPath(str), Path(str)
Inpired from goessner.net/articles/JsonPath.

The syntax to write paths is similar to XPath but in a Json way. The following XPath expression:

/store/book[1]/title

would look like (dot notation)

store.book[0].title

or (bracket notation)

['store']['book'][0]['title']

The dot (.) token is used to access the object elements:

ex: object.key

The bracket ([]) token is used to access array or object elements:

  • In array: cities[0]
  • In object: ["city"]["name"] or ['city']['name']

The quote (" or ') is used to introduce a literal when the element is being written in bracket notation:

ex:["first"]["second"] = ['first']['second'] = first.second

JSONPath allows the wildcard symbol * for member names and array indices and the recursive descent operator ..

*.book[0].title => returns first book's title from any element, not only store
..title => returns all titles
store.book[*].title => returns all titles

Negative value in an array index token (the number between [] brackets) suggests position from end of the array:

..book[-1] => last book

[!WARNING] Values exceeding the array size will raise "out of range" errors!

[!TODO]
the array slice syntax proposal [start:end:step] is not supported yet!

Specific to Array

Add(element)
Where "element" is a variant to be added to the list/array.

Notes:

  • If the element is VarJSON, the JSON value is cloned.
  • If the element is an Array, it is added as one element of array type.

Append(element)
Very similar with "Add". In most cases, it has the same behavior and appends that element to the list. However, if the element is an array, all its elements are added to the list (instead of one array element).

Remove(i), Delete(i)
Removes the element on position "i".
May raise "out of range" errors!

Specific to Object

Items(name), Values(name), Get(name)
Specific for objects, these functions accept a string value representing the key name and returns the Variant Value for that property.

[!WARNING] if the property with specified name is not found it raises an exception!

Remove(name), Delete(name)
Removes the pair matching the key name.
If there is no matching key name, nothing is removed/deleted!

Names[i], Keys[i]
Returns the key name on position i.
May raise "out of range" errors!

HasProperty(name), HasAttribute(name), HasName(name), HasKey(name)
Checks if the object has a property with the specified name. True if property exists, False otherwise.

Names, Keys

Returns a variant array with the key names.

General functions

VarIsJSON, VarIsJsonObject, VarIsJsonArray
Boolean functions to check if the provided variant is JSON (Object or Array).

VarJSONCreate
See Create JSON Variant

ExtractJsonValue(Variant): TJSONValue;
Creates a new TJSONValue (clone) from the variant! The ownership is passed to the caller => pay attention to memory leaks...

PeekJsonValue (Variant) : TJSONValue;
Exposes the JSON Value used by a VarJSON. Returns nil for non JSON variants!
Ownership is NOT transfered! DO NOT FREE!!!

PeekJsonObject(Variant) : TJSONObject;
To avoid type casting, if you know the result is a JSON object.

PeekJsonArray (Variant) : TJSONArray;
To avoid type casting, if you know the result is a JSON array.

NamesOf(Variant): TStringDynArray;
Returns a dynamic string array with the key names. Similar with Names property but easier to use in a for in loop:

pascal
    for name in NamesOf(json) do
      Log(name + '=' + VarToStr(json.Values(name)));

ValuesOf(Variant): TArray<Variant>;
Returns a dynamic Variant array with the values of the JSON Object/Array. This is a copy (clone) of the source JSON variant values!

📜 License

Dual-licensed:

  • GPL v3 licence - Use for free, under the restrictions of the GPL v3 licence, or
  • Purchase a commercial licence. Which grants you the right to use in your own applications, royalty free, without any requirement to disclose your source code nor any modifications to any other party. Contact ZenDev4D for details.

Please consider supportting this project by donating ("Buy me a coffee", "Thanks.dev", "Paypal").

🙏 Contributions

Feel free to submit pull requests, report bugs or suggest improvements. This project is intended to stay small but powerful.