SkillAgentSearch skills...

McJSON

A Delphi / Lazarus / C++Builder simple and small class for fast JSON parsing.

Install / Use

/learn @hydrobyte/McJSON
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

McJSON

A Delphi / Lazarus / C++Builder simple and small class for fast JSON parsing.

Motivation

Some points of interest:

  • Simple Object-Pascal native code using TList as internal data structure.
  • Single-pass string parser.
  • Compatible (aimed):
    • Delphi 7 up to now.
    • Lazarus.
    • C++Builder 2006 up to now.
  • Tested with:
    • BDS 2006 (Delphi and BCP)
    • Lazarus 2.3.0 (FPC 3.2.2)
    • C++Builder XE2 and 10.2.
  • Just one unit (McJSON), just one class(TMcJsonItem).
  • Inspired by badunius/myJSON.
  • Improved parser after applying Tan Li Hau's article.
  • Performance tests using C++Builder and comparing:

Examples

Object-Pascal Example

uses
  McJSON;
...  
function Test99(out Msg: string): Boolean;
var
  Json: TMcJsonItem;
  i: Integer;
begin
  Msg := 'Test: Github readme.md content';
  Json := TMcJsonItem.Create();
  try
    try
      // add some pairs.
      Json.Add('key1').AsInteger := 1;
      Json.Add('key2').AsBoolean := True;
      Json.Add('key3').AsNumber  := 1.234;
      Json.Add('key4').AsString  := 'value 1';
      // add an array
      Json.Add('array', jitArray);
      for i := 1 to 3 do
        Json['array'].Add.AsInteger := i;
      // save a backup to file
      if (Json['array'].Count = 3) then
        Json.SaveToFile('test99.json');
      // remove an item
      Json.Delete('array');
      // oops, load the backup
      if (Json.Count = 4) then
        Json.LoadFromFile('test99.json');
      // test final result
      Result := (Json.AsJSON = '{"key1":1,"key2":true,"key3":1.234,"key4":"value 1","array":[1,2,3]}');
    except
      Result := False;
    end;
  finally
    Json.Free;
  end;
end;

Will produce \test\test99.json:

{
  "key1": 1,
  "key2": true,
  "key3": 1.234,
  "key4": "value 1",
  "array": [
    1,
    2,
    3
  ]
}

C++Builder Example

#include "McJson.hpp"
...
bool Test99(AnsiString& Msg)
{
  bool Result;
  TMcJsonItem* Json = NULL;
  Msg = "Test: Github readme.md content";
  Json = new TMcJsonItem();
  try
  {
    try
    { // add some pairs.
      Json->Add("key1")->AsInteger = 1;
      Json->Add("key2")->AsBoolean = true;
      Json->Add("key3")->AsNumber  = 1.234;
      Json->Add("key4")->AsString  = "value 1";
      // add an array
      Json->Add("array", jitArray);
      for (int i = 1; i <= 3 ; i++)
        Json->Values["array"]->Add()->AsInteger = i;
      // save a backup to file
      if (Json->Values["array"]->Count == 3)
        Json->SaveToFile("test99.json");
      // remove an item
      Json->Delete("array");
      // oops, load the backup
      if (Json->Count == 4)
        Json->LoadFromFile("test99.json");
      // test final result
      Result = (Json->AsJSON ==
                "{\"key1\":1,\"key2\":true,\"key3\":1.234,\"key4\":\"value 1\",\"array\":[1,2,3]}");      
    }
    catch(...)
    {
      Result = false;
    }
  }
  __finally
  {
    if (Json) delete (Json);
  }
  return (Result);
}

Use Cases

Please considere read Unit Tests in test folder for a complete list of McJSON use cases.

Parse a JSON string

Just use the AsJSON property

var
  N: TMcJsonItem;
begin  
  N := TMcJsonItem.Create;
  N.AsJSON := '{"i": 123, "f": 123.456, "s": "abc", "b": true, "n": null}';
  // use N here
  N.Free;
end;  

If you want to check if a JSON string is valid:

Answer := N.Check( '{"i":[123}' ); // Answer will be false

The Check method will not raise any exception. The example above will catch and hide the Error while parsing text: "expected , got }" at pos "10" exception. If you need to catch and manage exceptions, use CheckException like:

try
  Answer := N.CheckException( '{"k":1, "k":2}' ); // Answer will be false
except
  on E: Exception do
  begin
    // Error while parsing text: "duplicated key k" at pos "11" 
  end;
end;    

Paths

McJSON allows a simple way to access items through paths. We can use '/', '\' or '.' as path separators.

N.AsJSON := '{"o": {"k1":"v1", "k2":"v2"}}';
// access and change second object's value
N.Path('o.k2').AsString := 'value2';

Results in:

{
   "o": {
      "k1":"v1",
      "k2":"value2"
   }
}

Note that Path() does not accept indexes yet, like this:

N.AsJSON := '{"o": [{"k1":"v1"}, {"k2":"v2"}]';
N.Path('o[1].k2').AsString := 'value2';

Property shorteners

Since version 1.0.4 McJSON allows to use property shorteners like in Andreas Hausladen's Json Data Objects.

// access (automatic creation as in JDO)
Obj.S['foo'] := 'bar';
Obj.S['bar'] := 'foo';
// array creation, Obj is the owner of 'array'
Obj.A['array'].Add.AsInteger := 10;
Obj.A['array'].Add.AsInteger := 20;
// object creation, 'array' is the owner of ChildObj
ChildObj := Obj['array'].Add(jitObject);
ChildObj.D['value'] := 12.3;
// array creation, ChildObj is the owner of 'subarray'
ChildObj.A['subarray'].Add.AsInteger := 100;
ChildObj.A['subarray'].Add.AsInteger := 200;

Results in:

{
   "foo":"bar",
   "bar":"foo",
   "array":[
      10,
      20,
      {
         "value":12.3,
         "subarray":[
            100,
            200
         ]
      }
   ]
}

Array or object items

Here is how to access all items (children) of a JSON object and change their value type and content.

N.AsJSON := '{"o": {"k1":"v1", "k2":"v2"}}';
// type and value: from string to integer
for i := 0 to N['o'].Count-1 do  
  N['o'].Items[i].AsInteger := i+1;   

Results in:

{
   "o": {
      "k1":1,
      "k2":2
   }
}

Shortener for array item access

We can use the Items[index] and Values['key'] properties to access items inside objects and arrays. Since version 0.9.5, we can use the At(index, 'key') or At('key', index) as shorteners.

N.AsJSON := '{"a": [{"k1":1,"k2":2},{"k1":10,"k2":20}]}';
// how to access k2 of second object.
i := N['a'].Items[1].Values['k2'].AsInteger; // i will be equal to 20
i := N['a'].Items[1]['k2'].AsInteger;        // uses the Values[] as default property
i := N['a'].At(1, 'k2').AsInteger;           // shortener: index, key
i := N.At('a', 1)['k2'].AsInteger;           // shortener: key, index

And there are other uses without the key parameter:

N.AsJSON := '{"k1":1,"k2":2,"k3":3,"k4":4}';
i := N.Items[2].AsInteger; // i will be equal to 3
i := N.At(2).AsInteger;    // shortener: just index
i := N.At('k3').AsInteger; // shortener: just key

Enumerate

Using Delphi enumerator you can browse item's object children and values.

var
  N, item: TMcJsonItem;
begin
  N := TMcJsonItem.Create;
  N.AsJSON := '{"o": {"k1":"v1", "k2":"v2"}}';
  for item in N['o'] do
    // use item here, e.g. item.Key, item.Value, item.AsString

Object and array value setters

Change all values of an object with multiple items. Not so common out there.

N.AsJSON := '{"o": {"k1":"v1", "k2":"v2"}}';
N['o'].AsString := 'str';

Results in:

{
   "o": {
      "k1": "str",
      "k2": "str"
   }
}

And if it is necessary to change the type of o:

N['o'].ItemType := jitValue;
N['o'].AsString := 'str';

Results in:

{
  "o": "str"
}

Object and array type convertions

Convert from array to object type and vice-versa. Also, not so common out there.

N.AsJSON := '{ "k1": ["1", "2"], "k2": {"1": "a", "2": "b"} }';
N['k1'].ItemType := jitObject; // convert array to object with items
N['k2'].ItemType := jitArray ; // convert object with items to array 

Results in:

{
   "k1": {
      "0": "1",
      "1": "2"
   },
   "k2": [
      "a",
      "b"
   ]
}

Add objects

To add objects inside others, simply use the Add() method:

M.Add('k1').AsString := 'v1';
P.Add('k2').AsString := 'v2';
N.ItemType := jitArray;       // important: array of objects
N.Add(M);
N.Add(P);

Note that the N object must be set to jitArray in order to receive two or more objects.

Results in:

[
  {
    "k1": "v1"
  },
  {
    "k2": "v2"
  }
]

Add pairs

To add key:value pairs inside other objects, use the AddPair() method:

M.Add('k1').AsString := 'v1';
P.Add('k2').AsString := 'v2';
N.ItemType := jitObject;      // important: must be an object
N.AddPair(M.Items[0]);
N.AddPair(P.Items[0]);

Note that the N object must be set to jitObject in order to receive key:value pairs.

Results in:

{
  "k1": "v1",
  "k2": "v2"
}

Insert items

Insert some items using keys and position.

P.Insert('c', 0).AsInteger := 3;
P.Insert('b', 0).AsInteger := 2;
P.Insert('a', 0).AsInteger := 1;

Results in:

{
  "a": 1,
  "b": 2,
  "c": 3
}

Also, it is possible to insert objects in arrays.

Q.AsJSON := '{"x":0}';
P.ItemType := jitArray;
P.Insert(Q, 1);

Results in:

[
  1, 
  {
    "x": 0
  }, 
  2, 
  3
]

Important: since version 0.9.3, Add() and Insert() will clone arguments of type TMcJsonItem. So, we have to free memory for Q too:

P.Free;
Q.Free;

Escape strings

Since version 1.0.5 strings can be escaped with McJsonEscapeString() helper function:

N.AsJSON := '{"path": ' + McJsonEscapeString('\dir\subdir') + '}';  

Results in:

{
  "path": "\\dir\\subdir"
}

In version 1.0.6 was introduced the TJEscapeType enum used in `McJso

Related Skills

View on GitHub
GitHub Stars67
CategoryDevelopment
Updated3mo ago
Forks20

Languages

Pascal

Security Score

97/100

Audited on Dec 27, 2025

No findings