JsonX4 is a Delphi JSON/YAML to Objects, Objects to JSON/YAML parser. It is fast (1M/s Ops), light still simple to use It supports Delphi from 10.3 on all platforms. And, of course, 100% of the json format specification are supported...
- This project is sponsored by EA4D "Ebay Api 4 Delphi" (https://www.ea4d.com)
- Projetcs using JsonX4 : qBit4DelphiV2 - qNOXifyV2 https://github.com/bnzbnz/qBit4DelphiV2 a qbittorent API for Delphi.
- Contact : Laurent MEYER JsonX4@lmeyer.fr
- qBit4DelphiV2 - qNOXifyV2 : https://github.com/bnzbnz/qBit4DelphiV2 a qbittorent API for Delphi.
- ConsoAPI : (https://github.com/bnzbnz/ConsoAPI4Delphi) a Delphi API to get data from Enedis (French)
- Clone the JsonX4 repository, demos should work out of the box.
- Add the units from the JsonX4/uJsonX4 folder to your project.
Example : using primitives (Demo01)
TPrimitives = class(TJ4Object)
Str: TValue; // As Str
Bool: TValue; // As Bool
Num: TValue; // as Int64
end;
Primitives := TPrimitives.Create;
Primitives.Str := 'testing π';
Primitives.Bool := True;
Primitives.Num1 := -999;
Primitives.Num2 := 999;
Primitives.Num3 := 2.2;
Primitives.Num4 := 22.22;
Primitives.NullStr := Nil;
JX4 will take care of all owned objects (Constructor/Destrutor), for exmaple 'Primitives.Str" is created and will be destroyed automatically (or pooled) , you don't have take care of it!
Json := Primitives.ToJson([]); // Serialization
{"Str":"testing π","Bool":true,"Num":-99}
NewPrimitives := TJX4Object.FromJSON<TPrimitives>(Json);
By deserializing from the Json string we made a copy of the TPrimtive object
Result =
DeserPrim.Str.AsString ==> 'testing π';
DeserPrim.Bool.AsBoolean ==> True;
DeserPrim.Num.AsOrdinal ==> -99;
Example : using inner classes (Demo02)
TSubClassDemo = class(TJX4Object)
X: TValue;
PClass: TPrimitives
end;
TInnerObjectDemo = class(TJX4Object)
S: TValue;
SubClass: TSubClassDemo; // a class TJX4Object
end;
Json := Demo.ToJson([joNullToEmpty]); // Remove null fields
{"S":"~~π~~","SubClass":{"X":222}}
Obviously you may deserialize the Json string to an TInnerObjectDemo Object. Again, JX4 will create the inner classes for you. An inner class may contains any number of sub inner classes...
It's where JX4 excel ! You can create any complex types
TObjectDemo = class(TJX4Object)
Str: TValue;
Keys: TJX4ValList; // an array(List) of strings : TArray<of any primitives>
Nums: TJX4ValDict; // An dictionary of any primitives (<string, number>) *JSON allows only strings as key
Primitives: TJX4List<TPrimitive>; // A list of TPrimitives (object)
SLists: TJX4List<TJX4ValList>; // A list of primitive Lists
PDicList: TJX4List<TJX4Dic<TJX4List<TPrimitive>>>;// ouch ! A List of dictionaries of TPrimitives Objects Lists !!!
end;
Please note that JX4 uses TLists instead of arrays, TList being way easier to use. Filled with value, serializing this (joNullToEmpty), will give you ;
{"Str":"~~π~~","Keys":["Q W E R T Y","A Z E R T Y"],"Nums":{"Double":33.33,"Currency":44.44,"Int64":2222,"Int":1111},"Primitives":[{"Bool":true,"I":111},{"Bool":false,"Dble":333.33}],"SLists":[["TTT","OOO"],["XXX","YYY","ZZZ"]],"PDicList":[{"DicVal1":[{"Str":"Boolean1","Bool":true}],"DicVal2":[{"Str":"Boolean2","Bool":true}]},{"DicVal1":[{"Str":"Boolean1","Bool":true}],"DicVal2":[{"Str":"Boolean2","Bool":true}]}]}
This is perfectly deserializable back to a TObjectDemo Object. You may validate this json string at : https://jsonformatter.org/json-parser
JX4 is able to handle any type of Objects as long as they implement 4 mandatory public methods, without any inheritence...
TJSONableStringList = class(TStringList) // A SringList to be parsed.
private
FIsManaged: Boolean;
public
procedure JSONCreate(AManaged: Boolean); // After the constructor being called, JX4 will tell the object if it is managed
function JSONDestroy: Boolean; // see Demo04
function JSONSerialize(AIOBlock: TJX4IOBlock): TValue;
procedure JSONDeserialize(AIOBlock: TJX4IOBlock);
// Optionals
procedure JSONClone(ADestObj: TObject; AOptions: TJX4Options);
function JSONMerge(AMergedWith: TStringList; AOptions: TJX4Options): TValue;
end;
TDemoContainer = class(TJX4Object)
public
StringList : TJSONableStringList; // Creation and destruction will be handle automatically
[JX4Unmanaged]
StringListNotManaged : TJSONableStringList; // NOT MANAGED : You HAVE to take care of the Creation/Destruction of this Object;
end; // It will still be serialized/deserialized and created if necessary (clone for ex.)
MyList := TJSONableStringList.Create; // In your code, create and use your own Object
...Fill the List
Obj := TDemoContainer.Create; // Create the container
Obj.StringList.Add('A'); // StringList has been created automagically
... Serialize / Deserialize
Obj.Free // will not destroy MyList
MyList.Free; // When necessary
So, you will be able to ser/deserialize any object by adding to them the corresponding methods.
TDemo = class(TJX4Object)
[TJX4Required]
Str: TValue; // A value is required when serializing (Exception)
[TJX4Name('#href')] // a name of the matching json name
HrefVar: TValue; // a name of the json value mapped to "Bool"
[TJX4Default('22')] // a defualt value to be used at deserialization, if the field is null
Num1: TValue;
__23href2: TValue; // name encoding : __23href = #hef ('_'+'_'+Hex('#')+'href')
[TJX4Default('true')] // ^-- Header
[TJX4Name('NewMix')]
Mix: TValue;
end;
{"Str":"Need a Value","#href":"http://","Num1":22,"#href2":"auto enc/dec oding","NewMix":true}
As simple as that :
TQuestion = class(TJ43Object)
question: TValue;
options: TJX4ValList;
answer: TValue;
end;
TGame = class(TJX4Object)
quiz: TJX4Dic<TJX4Dic<TQuestion>>; // << Double dictionaries !!
end;
var GameStr :=
'''
quiz:
sport:
q1:
question: Which one is correct team name in NBA?
options:
- New York Bulls
- Los Angeles Kings
- Golden State Warriros
- Huston Rocket
answer: Huston Rocket
maths:
q1:
question: 5 + 7 = ?
options:
- '10'
- '11'
- '12'
- '13'
answer: '12'
q2:
question: 12 - 8 = ?
options:
- '1'
- '2'
- '3'
- '4'
answer: '4'
''';
var Game := TJX4Object.FromTAML<TGame>(GameStr); // Get the Object from YAML
Memo1.Text := TJX4Object.Format( TJX4Object.ToJSON(Game) ); // Get the Json from the Object, and print the formated result
Memo1.Lines.Add('');
Memo1.Lines.Add('Questions - Options :');
for var LPk1 in Game.quiz do //Dump Questions - Options
for var LPk2 in LPk1.Value do
begin
Memo1.Lines.Add(LPk1.Key + ' - ' + LPk2.Value.question.V +' : ');
for var LP in LPk2.Value.options do
Memo1.Lines.Add(' ' + LP.Value);
end;
Game.Free;
In this example we read, serialize, clone (RTTI/Meerging), deserialize and finally save a large ebay's aspects JSON and YAML file (around 1M json fields) You will be able to benchmark and compare the output generated json file 'jsx4.json' vs 'aspects100.json' the original ebay's on. (You'll find also the generated jsx4.yml file)
Loading ebay's Aspects json file :
Stream size: 14,358.14 KB
==> 14 ms
Convert Json String to JSX4 Objects (Deserialize):
==> 1405 ms
==> 10,219.32 KB/s
JSX4 Object Cloning (by RTTI):
==> 1164 ms
==> 12,324.59 KB/s
JSX4 Object Cloning (by Merging):
==> 1265 ms
==> 11,350.31 KB/s
Revert JX4 Objects to Json String (Serialize)):
==> 1129 ms
==> 12,706.32 KB/s
Free Json Objects :
Freed in 860 ms
Saving Cloned Json file (jsx4.json) :
Stream size: 14,358.14 KB
==> 13 ms