McJSON
A Delphi / Lazarus / C++Builder simple and small class for fast JSON parsing.
Install / Use
/learn @hydrobyte/McJSONREADME
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
node-connect
345.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
106.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
