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 10.2.
- Just one unit (
McJSON
), just one class(TMcJsonItem
). - Inspired by badunius/myJSON.
- Performance tests using C++Builder and comparing:
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').ItemType := 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('example.json');
// remove an item
Json.Delete('array');
// oops, load the backup
if (Json.Count = 4) then
Json.LoadFromFile('example.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\example.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")->ItemType = jitArray;
for (int i = 1; i <= 3 ; i++)
Json->Items["array"]->Add()->AsInteger = i;
// save a backup to file
if (Json->Items["array"]->Count == 3)
Json->SaveToFile("example.json");
// remove an item
Json->Delete("array");
// oops, load the backup
if (Json->Count == 4)
Json->LoadFromFile("example.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);
}
Performance Tests
A performance test have been done with the original myJSON
, LkJson
, JsonTools
and uJSON
units. Here is a summary of the tests.
- Generate a JSON with 50k items like:
{... {"keyi":"valuei"}... }
- Save to file.
- Parse from memory (copy object forcing a parse).
- Load from file (and parsing).
- Access 1k items randomly.
And about the compiler and machine used:
- C++Builder VCL examples built with BDS 2006 (the older version I have).
- Very old 32 bits machine: Intel Core 2 CPU T5500 1.66GHz 4 GB RAM.
The next table summarizes the results1:
Library | Generate | Save | Parse | Load | Access | Total |
---|---|---|---|---|---|---|
McJSON |
.08 s | .09 s | .11 s | .16 s | .54 s | 1.05 s |
LkJson |
.30 s | .13 s | .47 s | .36 s | .00 s | 1.22 s |
JsonTools |
48.00 s | .70 s | 39.00 s | 40.00 s | .48 s | 1.2 min |
myJSON |
50.00 s | .07 s | 5.1 min | 7.7 min | 1.60 s | 13.1 min |
uJSON 2 |
18.6 min | 20.1 min | 17.5 min | 4.31 s | 53.02 s | 57.6 min |
myJSON
Notes about - Performance deteriored due the recurrent use of wsTrim().
- Generate using:
Json->Item["key"]->setStr("value")
. - Parse using:
JsonP->Code = Json->getJSON()
.
LkJson
Notes about - Good performance generating and parsing and even better with random access due to "Balanced Search Tree"
TlkBalTree
. - TLkJSONBase and other derivated classes force to cast objects using the "as" operator. In C++Builder, this requires
dynamic_cast
making the code verbosy. - Generate using:
Json->Add("key", "value")
. - Parse using:
JsonP = dynamic_cast<TlkJSONObject*>(TlkJSON::ParseText(NULL, TlkJSON::GenerateText(NULL, Json)))
.
JsonTools
Notes about - Very nice and interesting code focused on the concept of Tokens.
- Also uses TList as internal data structure.
- It needs a performance review.
- Generate using:
Json->Add("key", "value")
. - Parse using:
JsonP->Value = Json->AsJson
.
uJSON
Notes about - Less verbosy in C++ than
LkJson
, but the colection of classes also will force casting withdynamic_cast
. - Uses TStringList as a "Hash Map" [string] -> [object address]. The comas here is because I think the string entry is not a true hash within TStringList.
- In some aspectes, the methods interface might became puzzling.
- It needs a performance review.
- This unit is used in other projects, e.g. Diffbot API Delphi Client Library (same author).
- Generate using:
Json->put("key", "value")
. - Parse using:
JsonP = new TJSONObject(Json->toString())
. SaveToFile
doesn't exist, so it has usedTStringList->SaveToFile()
after fillingText
withJson->toString()
.
McJSON
Notes about - Good performance, but not the better about random access due to the use of TList.
- Simple and smart interface using "AsXXX" getters and setters (not invented here).
- Generate using:
Json->Add("key")->AsString = "value"
. - Parse using:
JsonP->AsJSON = Json->AsJSON
.