GithubHelp home page GithubHelp logo

hydrobyte / mcjson Goto Github PK

View Code? Open in Web Editor NEW
55.0 9.0 19.0 177 KB

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

License: MIT License

Pascal 100.00%
delphi delphi7 lazarus cpp-builder json json-parser

mcjson's Introduction

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"
   ]
}

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 McJsonEscapeString() with these escape levels:

  • jetNormal : escapes #8 #9 #10 #12 #13 " \.
  • jetStrict : Normal + /.
  • jetUnicode : Strict + \uXXXX.
  • jetNone : backwards compatibility.

These levels are inspired by Lazarus' helper function StringToJSONString() from library fpjson.

Inspect the content of an object

Let's see how to inspect all the inner data structure, types and values of a TMcJsonItem object.

//---------------------------------------------------------------------------
void
TFormMain::Inspect(TMcJsonItem* AMcJItem, AnsiString Ident)
{
  if (!AMcJItem) return;
  // log current
  MyLog( Ident + ItemToStr(AMcJItem) );
  // log child
  if ( AMcJItem->HasChild )
  {
    Ident = "  " + Ident;
    for (int i=0; i < AMcJItem->Count; i++)
    { // use Value not Child because are note using Key[].
      Inspect( AMcJItem->Items[i], Ident );
    }
  }
}
//---------------------------------------------------------------------------
String
TFormMain::ItemToStr(TMcJsonItem* AMcJItem) const
{
  String Ans = "";
  if (AMcJItem)
    Ans =             AMcJItem->GetTypeStr() +
          "; "      + AMcJItem->GetValueStr() +
          "; Key="  + AMcJItem->Key +
          "; Value="+ AMcJItem->Value +
          "; JSON=" + AMcJItem->AsJSON;
  return (Ans);
}
//---------------------------------------------------------------------------

And using a example like testInspect.json:

{
   "foo": "bar",
   "array": [
      100,
      20
   ],
   "arrayObj": [
      {
         "key1": 1.0
      },
      {
         "key2": 2.0
      }
   ],
   "Msg": [
      "#1 UTF8 example: motivação",
      "#2 Scapes: \b\t\n\f\r\\uFFFF\"\\"
   ]
}

Calling Inspect() with a Json object loaded with testInspect.json:

TMcJsonItem* Json = new TMcJsonItem();
if (Json)
{
  Json->LoadFromFile("testInspect.json");
  Inspect(Json);
  delete (Json);
}

Results in:

object; string; Key=; Value=; JSON={"foo":"bar","array":[100,20],"arrayObj":[{"key1":1.0},{"key2":2.0}],"Msg":["#1 UTF8 example: motivação","#2 Scapes: \b\t\n\f\r\u\"\\"]}
   value; string; Key=foo; Value=bar; JSON="foo":"bar"
   array; string; Key=array; Value=; JSON="array":[100,20]
     value; number; Key=; Value=100; JSON=100
     value; number; Key=; Value=20; JSON=20
   array; string; Key=arrayObj; Value=; JSON="arrayObj":[{"key1":1.0},{"key2":2.0}]
     object; string; Key=; Value=; JSON={"key1":1.0}
       value; number; Key=key1; Value=1.0; JSON="key1":1.0
     object; string; Key=; Value=; JSON={"key2":2.0}
       value; number; Key=key2; Value=2.0; JSON="key2":2.0
   array; string; Key=Msg; Value=; JSON="Msg":["#1 UTF8 example: motivação","#2 Scapes: \b\t\n\f\r\uFFFF\"\\"]
     value; string; Key=; Value=#1 UTF8 example: motivação; JSON="#1 UTF8 example: motivação"
     value; string; Key=; Value=#2 Scapes: \b\t\n\f\r\uFFFF\"\\; JSON="#2 Scapes: \b\t\n\f\r\uFFFF\"\\"

A note about empty keys

Since version 0.9.0, empty keys will be parsed and checked withou errors:

N.AsJSON := '{"": "value"}';

And ToString() will produce a valid JSON object:

{
  "": "value"
}

Internally, it will use the C_EMPTY_KEY constant string as content of the fKey field.

A note about line breaks

Since version 0.9.2, strings with not escaped line breakes will be parsed with errors:

N.AsJSON := '{"key": "value' + #13 + '"}';

Will raise exception:

Error while parsing text: "line break" at pos "14"

Load from and Save to Files

McJSON can load from ASCII and UTF-8 files (with or without BOM). See LoadFromFile method. The SaveToFile method will write using UTF-8 encoding. Note: since vertion 1.0.4, the test project's source code in Lazarus was converted to UTF-8, so the asUTF8 parameter was set to false.

Known issues

The world is not perfect and neither am I. Here are some known issues:

  • As TMcJsonItem objects are instantiated in hierarchical structure using lists fChild, there is a problem to create fields that propagate automatically between items. A solution under study tries to create a new parent class TMcJson which objects will be like roots and have TMcJsonItem objects as its children.
  • Trying to follow and confirm the specification using JSONLint.

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
McJSON2 .11 s .07 s .12 s .09 s .83 s 1.25 s
LkJson2 .30 s .11 s .47 s .36 s .01 s 1.24 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 18.6 min 20.1 min 17.5 min 4.31 s 53.02 s 57.6 min

Notes about McJSON

  • 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.

Notes about LkJson

  • 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))).

Notes about JsonTools

  • 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.

Notes about myJSON

  • Performance deteriored due the recurrent use of wsTrim().
  • Generate using: Json->Item["key"]->setStr("value").
  • Parse using: JsonP->Code = Json->getJSON().

Notes about uJSON

  • Less verbosy in C++ than LkJson, but the colection of classes also will force casting with dynamic_cast.
  • Uses TStringList as a "Hash Map" [string] -> [object address]. The quotation marks here is because I think the string entry is not a true hash within TStringList.
  • In some aspects, the methods interface might became puzzling.
  • It needs a performance review.
  • With uJSON, there seems to be a performance problem related to toString().
  • 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 used TStringList->SaveToFile() after filling Text with Json->toString().

Footnotes

  1. Metric: average time in seconds (s) for 5 consecutive executions. Total is the average of partial tests. Some results converted to minutes (min).

  2. Version 1.0.5. Improved Test JSON 0.9.0 project that will be released soon. 2

mcjson's People

Contributors

dgobrito avatar hydrobyte avatar totyaxy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mcjson's Issues

How check path exist by not throw error

How to check nill of specific paht which throw to show error?
such I want to check
N.Path('o.k2')
or
N['o']['k2']
= nil ?

but now if path not exist McJSON show error automatically, but I don't want that.

I just want to be able to check for myself whether it's there or not. Even if there is, I will proceed with one thing. But if you don't have it, don't do another thing, for example.

if N['o']['k2']  = nil then
 // do something
else
 // do something else ;

or

if N['o']['k2'].ItemType  = jitNone then
 // do something
else
 // do something else ;

Note : for JsonDataObjects can do this

if N['o']['k2'].Typ = jdtNone then
 // do something
else
 // do something else ;

Please guide me for this
Thank you

McJSONEscapeString TAB problem

Hi!

I'v found small error in the new function:

procedure TForm1.Button1Click(Sender: TObject);
const
  cSample = 'Fisch'+#9;
var
  s: string;
begin
  s := McJSONEscapeString(cSample, jetNormal);
  Memo1.Lines.Add('MC: "'+s+'"');
  s := McJSONUnEscapeString(s);
  if s <> cSample then Memo1.Lines.Add('MC error!')
  else Memo1.Lines.Add('MC OK!');

  s := StringToJSONString(cSample);
  Memo1.Lines.Add('FP: "'+s+'"');
  s := JSONStringToString(s);
  if s <> cSample then Memo1.Lines.Add('FP error!')
  else Memo1.Lines.Add('FP OK!');
end;      

Result:

Memo1
MC: "Fisch\	"
MC error!
FP: "Fisch\t"
FP OK!

Improve test case

Just looking at the test function Test99 and I note that it performs a save followed by a load, however, this doesn't prove if the load actually worked. An improvement would be to perform a change between the save and load, but not a complete clear. A possible change could be the addition of an extra item before the load, an item that then should not be present in the resulting JSON for the test to pass.

McJsonEscapeString (overzealous)

Hi!

Very-very thanks for this feature, but it's very overzealous as it also converts all accented characters, which isn't necessarily lucky since json is a human-readable/modifiable format (It is used by translators, for example).

So, I think this code uneccesary:

if ((Integer(c) <  32) or (Integer(c) > 126)) then
   Result := Result + ESCAPE + 'u' + IntToHex(Integer(c), 4)

But if you need this, I suggest more parameters, for example:

function McJsonEscapeString(const aStr: string; const aEscapeNonEnglishChars: boolean = False): string;
...
      begin
        if  (integer(c) < 32) or ((integer(c) > 126) and aEscapeNonEnglishChars) then
          Result := Result + ESCAPE + 'u' + IntToHex(integer(c), 4)
        else
          Result := Result + c;
      end;
...

Range check error

Hi!

This simple code cause range check error (in debug mode) at line 1155
(Lazarus 2.2.4 official, x64 on windows)

procedure TForm1.Button1Click(Sender: TObject);
var
N: TMcJsonItem;
begin
N := TMcJsonItem.Create;
try
N.AsJSON := '{"o": {"k1":"v1", "k2":"v2"}}';
ShowMessage(N.Path('o.k2').AsString);
finally
N.Free;
end;

end;

PrjTestMcJSON - many exception

Hi!

With release mode, the result is: "6 test failed"

With debug mode, I got many exceptions...

Lazarus 2.2.4 x64 official on Windows x64

Invalid char at pos 1 (UTF8 BOM support)

Hi!

Unfortunately, many utf8 json files contain a BOM, which McJSON cannot read. I solved this in Lazarus, of course, but I don't know how the code would work under Delphi (UTF16?), so I don't offer a pull request.
So my request/recommendation would be that you also support UTF8 BOM encoded files, at least for reading (I did it for writing as well, but it's not that important). The code itself is not complicated, because if a BOM is detected in LoadFromStream, you only need to position it after the BOM (but the reading moved it anyway).
UTF8 BOM: $EF $BB $BF
See: https://learn.microsoft.com/en-us/globalization/encoding/byte-order-mark
Since you also support Delphi, as far as I know, UTF16 encoding is the basis there, so then UTF16LE/BE BOMs could also be supported.

How fix warning compiler about string

Hello,

I gave the variable a JSON string value before adding the value to the JSON object with the AsJSON method, which caused a warning.
TestMcJSON_.zip [This if sample project]
But I get 33 warning messages after compilation.
about :

  • W1050 WideChar reduced to byte char in set expressions. Consider using 'CharInSet' function in 'SysUtils' unit
  • W1057 Implicit string cast from 'AnsiString' to 'string'
  • W1058 Implicit string cast with potential data loss from 'string' to 'AnsiString'

Plese advice me to fix these

Thank you

Check function does not return the wrong position

Hi!

I would like to use the check function, but the problem is that it hides the error position (without debugger).

function TMcJsonItem.Check(const aStr: string; aSpeedUp: Boolean): Boolean;
var
  aItem: TMcJsonItem;
begin
  aItem := TMcJsonItem.Create;
  try
    aItem.fSpeedUp := aSpeedUp;
    aItem.AsJSON   := aStr;
//    Result := True;
    Result := (aItem.AsJSON = trimWS(aStr));
  except
    Result := False;
  end;
  aItem.Free;
end;   

Idea, integer result:

function TMcJsonItem.Check(const aStr: string; aSpeedUp: Boolean): integer // return value is the wrong position...

and then the original function stay, and use it:

function TMcJsonItem.Check(const aStr: string; aSpeedUp: Boolean): booelan;
begin
  if Check(aStr, aSpeedup)<0 then Result:= true else Result:=false;
end;

... or other way, if you have time.

MCJsonUnEscapeString "/" and "\" missing after unescape

Hi!

Very thank you fro the new valuable functions, but I found a little error:

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s:='A / B';

  Memo1.Lines.Add(s);
  s:= MCJsonEscapeString(s);
  Memo1.Lines.Add(s);
  s:= MCJsonUnEscapeString(s);
  Memo1.Lines.Add(s);

  s:='A \ B';

  Memo1.Lines.Add(s);
  s:= MCJsonEscapeString(s);
  Memo1.Lines.Add(s);
  s:= MCJsonUnEscapeString(s);
  Memo1.Lines.Add(s);
end;                

result:

Memo1
A / B
A \/ B
A  B
A \ B
A \\ B
A  B

"/" and "" is missing after unescape.

Shorteners self-test result

Hi!

It's not a bugreport, only help for you (with "," separator):

[PASS] Test 01: parse simple object
[PASS] Test 02: parse simple array
[PASS] Test 03: parse simple sub object array
[PASS] Test 04: simple object value change
[PASS] Test 05: Add, Insert, Delete functions
[PASS] Test 06: object is nil
Error: Invalid index: get item by index 3
[PASS] Test 07: getters and setters
[FAIL] Test 08: numbers: scientific notation
Error: Can't convert item "number" with value "-1.23456789E-10" to "double"
[PASS] Test 09: escapes
[PASS] Test 10: invalid JSON
[PASS] Test 11: valid or unusual JSON
[FAIL] Test 12: type transformations
[FAIL] Test 13: Save and Load using files
[PASS] Test 14: constructors
[PASS] Test 15: Copy, Clone, IsEqual, Remove functions
[PASS] Test 16: exceptions
Error: Object reference is nil: get item by key "not"
Error: Object reference is nil: get item by key "not"
Error: Object reference is nil: get item by index 1
Error: Invalid item type: expected "object" got "value"
Error: Can't convert item "string" with value "123a" to "integer"
Error: Can't convert item "null" to "integer"
Error: Error while parsing text: "duplicated key k" at pos "14"
[PASS] Test 17: enumerators
[FAIL] Test 18: example like JsonDataObjects
[PASS] Test 19: At() shortener for array item access
[PASS] Test 20: key paths
[FAIL] Test: Github readme.md content
Error: Error while parsing text: "expected : got 3" at pos "48"

5 tests FAILED

With "." separator:

[PASS] Test 01: parse simple object
[PASS] Test 02: parse simple array
[PASS] Test 03: parse simple sub object array
[PASS] Test 04: simple object value change
[PASS] Test 05: Add, Insert, Delete functions
[PASS] Test 06: object is nil
Error: Invalid index: get item by index 3
[PASS] Test 07: getters and setters
[PASS] Test 08: numbers: scientific notation
[PASS] Test 09: escapes
[PASS] Test 10: invalid JSON
[PASS] Test 11: valid or unusual JSON
[PASS] Test 12: type transformations
[FAIL] Test 13: Save and Load using files
[PASS] Test 14: constructors
[PASS] Test 15: Copy, Clone, IsEqual, Remove functions
[PASS] Test 16: exceptions
Error: Object reference is nil: get item by key "not"
Error: Object reference is nil: get item by key "not"
Error: Object reference is nil: get item by index 1
Error: Invalid item type: expected "object" got "value"
Error: Can't convert item "string" with value "123a" to "integer"
Error: Can't convert item "null" to "integer"
Error: Error while parsing text: "duplicated key k" at pos "14"
[PASS] Test 17: enumerators
[PASS] Test 18: example like JsonDataObjects
[PASS] Test 19: At() shortener for array item access
[PASS] Test 20: key paths
[PASS] Test: Github readme.md content

1 tests FAILED

Lazarus 2.2.4 x64 (official installer) on Win10 x64

New empty key destroys the json structure

In practice, I also solve the configuration with MCJson. Therefore, due to new situations, unexpected errors also occur. If I add a key to an existing structure (asJson, LoadFromFile) (main key, without value, because it won't have any), it destroys the json, and next time it won't even be loaded.
It's possible that an empty key (category) shouldn't be added this way, but I don't think it should spoil the json structure.

procedure TForm1.Button1Click(Sender: TObject);
const
  fn = 'test.json';
var
  N: TMcJsonItem;
begin
  DeleteFile(fn);

  N := TMcJsonItem.Create;
  try
    N.AsJSON := '{"i": 123, "f": 123.456, "s": "abc", "b": true, "n": null}';

    //N.SaveToFile(fn);
    //N.LoadFromFile(fn);
    //Memo1.Lines.LoadFromFile(fn);

    N.Add('EmptyNewKey'); // <- this destroy json structure

    N.SaveToFile(fn);
    N.LoadFromFile(fn);
    Memo1.Lines.LoadFromFile(fn);

  except
    N.Free;
  end;
end; 

Slow json generation speed

Presumably due to my not quite correct code. I create a json structure this way:

Json.Add(IndexStr);
for x in Tx do
  Json.Path(IndexStr).Add(x.key).AsString := McJsonEscapeString(x.value);   

This about look this way (about 150k line):

 "200": {
    "Key1": "Value1",
    "Key2": "Value2",
    "Key3": "Value3"
  },

 "201": {
    "Key1": "Value1",
    "Key2": "Value2",
    "Key3": "Value3"
  },

I think the low generation speed is because of the path usage, can this be fixed somehow? For example
If I always write to the last one, the library doesn't need to search through the entire structure.

Slower shortener operation (but I realized this is normal because of the AddOrSet search)

Hi

I started to standardize the code from the traditional code to a shortener version (which had not yet been rewritten). The code below in the shortened version is approx. 20 times slower. It is possible that this is not how the use of the shortener code was invented (create object), but it makes sense to me and it works: ( "->" just an indicator in the code)

var
 Json: TMcJsonItem = nil;
 JsonDataObject: TMcJsonItem = nil;
 JsonIDObject: TMcJsonItem = nil;
 
begin
...
Json := TMcJsonItem.Create;
...
      JsonDataObject := Json.O[cJsonDataObject]; // Create object... ('JsonData')

      for i := 0 to Self.Count - 1 do
      begin
        IndexStr := IntToStr(Self.Data[i].Index);
        -> JsonIDObject := JsonDataObject.O[IndexStr]; // Create object... this 20 times slower than the next line:
        -> JsonIDObject := JsonDataObject.Add(IndexStr); // Create object... (I know it isn't a correct code, but see ***

        for lt in TLanguageTypes do
          JsonIDObject.Add(lt.KeyName).AsString := Self.Data[i].GetItem(lt);
      end;

What I also don't understand is that earlier you showed a code for creating an empty key (object) #17 (comment):

So the correct code for to create object, this (non shortener variant): ***

->JsonIDObject := JsonDataObject.Add(IndexStr, jitObject);

But no, because the compiler gives an error:

Error: Incompatible type for arg no. 2: Got "TJSONInstanceType", expected "TJItemType"

but the "jitObject" is TJItemType:

TJItemType  = (jitUnset, jitValue, jitObject, jitArray);

Thank you for your patience! I tried to describe everything as accurately as possible.

Check function speed problem

Hi!

There is an approx. My 11 MB json file, in the following structure (~20k block):

"<number>": {
       "key1": "value1",
       "key2": "value2",
       "key3": "value3",
       "key4": "value4",
     },
"<number+1>": {
       "key1": "value1",
       "key2": "value2",
       "key3": "value3",
       "key4": "value4",
     },

The Check function takes seconds. The interesting thing is that if I slightly put a main key (object) in front of it, the Check function will be much faster, which I find completely pointless.

So the structure will be like this:

"JsonData": {
"<number>": {
       "key1": "value1",
       "key2": "value2",
       "key3": "value3",
       "key4": "value4",
     },
"<number+1>": {
       "key1": "value1",
       "key2": "value2",
       "key3": "value3",
       "key4": "value4",
     },

If I mess up the structure somewhere, it finds the error in the same way, so I think checking the json structure of the first version can take so long because of some error.

HasKey exception

Hi!

If I want to check whether a particular key exists in a json file (eg config file), I can check it with the HasKey function. The only problem is that it gives an exception (object reference is nil), even though the boolean return value would be enough.
During development, it is very unfortunate if unnecessary exceptions interrupt the program execution.

Thank you!

  JsonConfig := TMcJsonItem.Create;
  JsonConfig.LoadFromFile(cConfigFile);

  if not(JsonConfig.HasKey(cOptions)) then;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.