Difference between revisions of "fcl-json"
(→Notes) |
(Added link to FCL page) |
||
(25 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
− | fcl-json | + | {{fcl-json}} |
− | == | + | = Info = |
− | |||
− | + | fcl-json is a [[JSON]] implementation. | |
+ | |||
+ | It contains units such as: | ||
+ | |||
+ | * fpjson: base unit which implements TJsonData and its children, e.g. TJsonObject | ||
+ | * jsonParser: implements TJsonParser, used in the [[#From_JsonViewer|From JsonViewer]] example below | ||
+ | * jsonConf: implements TJsonConfig which is handy to read/write application data from/to files | ||
+ | * jsonScanner: json source lexical analyzer | ||
Note: In fpjson, accessing e.g. SomeJSONObject.Integers['price'] may give a SIGSEGV/Access Violation if that integer variable does not exist. This is apparently intentional, see [http://bugs.freepascal.org/view.php?id=22273]. | Note: In fpjson, accessing e.g. SomeJSONObject.Integers['price'] may give a SIGSEGV/Access Violation if that integer variable does not exist. This is apparently intentional, see [http://bugs.freepascal.org/view.php?id=22273]. | ||
You'd have to use the Find method (available since FPC 2.6.2) to first check if the element ('price' in this example) exists. | You'd have to use the Find method (available since FPC 2.6.2) to first check if the element ('price' in this example) exists. | ||
− | + | = Streaming = | |
fcl-json contains the unit "fpjsonrtti" which is used to load objects (TObject instances) from or save them to JSON format. | fcl-json contains the unit "fpjsonrtti" which is used to load objects (TObject instances) from or save them to JSON format. | ||
See [[Streaming JSON]] for a short example. | See [[Streaming JSON]] for a short example. | ||
− | == | + | = Examples = |
+ | |||
+ | |||
+ | == Getting Started == | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | |||
+ | uses | ||
+ | fpjson, jsonparser; | ||
+ | |||
+ | procedure JSONTest; | ||
+ | var | ||
+ | jData : TJSONData; | ||
+ | jObject : TJSONObject; | ||
+ | jArray : TJSONArray; | ||
+ | s : String; | ||
+ | begin | ||
+ | |||
+ | // this is only a minimal sampling of what can be done with this API | ||
+ | |||
+ | // create from string | ||
+ | jData := GetJSON('{"Fld1" : "Hello", "Fld2" : 42, "Colors" : ["Red", "Green", "Blue"]}'); | ||
+ | |||
+ | // output as a flat string | ||
+ | s := jData.AsJSON; | ||
+ | |||
+ | // output as nicely formatted JSON | ||
+ | s := jData.FormatJSON; | ||
+ | |||
+ | // cast as TJSONObject to make access easier | ||
+ | jObject := TJSONObject(jData); | ||
+ | |||
+ | // retrieve value of Fld1 | ||
+ | s := jObject.Get('Fld1'); | ||
+ | |||
+ | // change value of Fld2 | ||
+ | jObject.Integers['Fld2'] := 123; | ||
+ | |||
+ | // retrieve the second color | ||
+ | s := jData.FindPath('Colors[1]').AsString; | ||
+ | |||
+ | // add a new element | ||
+ | jObject.Add('Happy', True); | ||
+ | |||
+ | // add a new sub-array | ||
+ | jArray := TJSONArray.Create; | ||
+ | jArray.Add('North'); | ||
+ | jArray.Add('South'); | ||
+ | jArray.Add('East'); | ||
+ | jArray.Add('West'); | ||
+ | jObject.Add('Directions', jArray); | ||
+ | |||
+ | end; | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | Note : its necessary to free jData when you have finished with it. Otherwise a memory leak is created ! | ||
+ | |||
+ | == Traversing Items == | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | uses | ||
+ | Classes, TypInfo, fpjson, jsonparser; | ||
+ | |||
+ | procedure JSONItems(Info: TStrings); | ||
+ | var | ||
+ | jData : TJSONData; | ||
+ | jItem : TJSONData; | ||
+ | i, j: Integer; | ||
+ | object_name, field_name, field_value, object_type, object_items: String; | ||
+ | begin | ||
+ | jData := GetJSON('{"A":{"field1":0, "field2": false},"B":{"field1":0, "field2": false}}'); | ||
+ | |||
+ | for i := 0 to jData.Count - 1 do | ||
+ | begin | ||
+ | jItem := jData.Items[i]; | ||
+ | |||
+ | object_type := GetEnumName(TypeInfo(TJSONtype), Ord(jItem.JSONType)); | ||
+ | object_name := TJSONObject(jData).Names[i]; | ||
+ | WriteStr(object_items, jItem.Count); | ||
+ | |||
+ | Info.Append('object type: ' + object_type + '|object name: ' + object_name + '|number of fields: ' + object_items); | ||
+ | |||
+ | for j := 0 to jItem.Count - 1 do | ||
+ | begin | ||
+ | field_name := TJSONObject(jItem).Names[j]; | ||
+ | field_value := jItem.FindPath(TJSONObject(jItem).Names[j]).AsString; | ||
+ | |||
+ | Info.Append(field_name + '|' + field_value); | ||
+ | end; | ||
+ | end; | ||
+ | |||
+ | jData.Free; | ||
+ | end; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Save/load dialog position/size == | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | uses | ||
+ | jsonConf; | ||
+ | |||
+ | procedure TfmMain.SaveOptionsPos; | ||
+ | var | ||
+ | c: TJSONConfig; | ||
+ | begin | ||
+ | c:= TJSONConfig.Create(Nil); | ||
+ | try | ||
+ | c.Filename:= GetAppPath(cFileHistory); | ||
+ | c.SetValue('/dialog/max', WindowState=wsMaximized); | ||
+ | if WindowState<>wsMaximized then | ||
+ | begin | ||
+ | c.SetValue('/dialog/posx', Left); | ||
+ | c.SetValue('/dialog/posy', Top); | ||
+ | c.SetValue('/dialog/sizex', Width); | ||
+ | c.SetValue('/dialog/sizey', Height); | ||
+ | end; | ||
+ | finally | ||
+ | c.Free; | ||
+ | end; | ||
+ | end; | ||
+ | |||
+ | procedure TfmMain.LoadOptionsPos; | ||
+ | var | ||
+ | nLeft, nTop, nW, nH: Integer; | ||
+ | c: TJSONConfig; | ||
+ | begin | ||
+ | c:= TJSONConfig.Create(Nil); | ||
+ | try | ||
+ | c.Filename:= GetAppPath(cFileHistory); | ||
+ | |||
+ | nLeft:= c.GetValue('/dialog/posx', Left); | ||
+ | nTop:= c.GetValue('/dialog/posy', Top); | ||
+ | nW:= c.GetValue('/dialog/sizex', Width); | ||
+ | nH:= c.GetValue('/dialog/sizey', Height); | ||
+ | SetBounds(nLeft, nTop, nW, nH); | ||
+ | |||
+ | if c.GetValue('/dialog/max', false) then | ||
+ | WindowState:= wsMaximized; | ||
+ | finally | ||
+ | c.Free; | ||
+ | end; | ||
+ | end; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Save/load TStringList == | ||
+ | |||
+ | <syntaxhighlight lang=pascal> | ||
+ | //Example of path: '/list_find' | ||
+ | |||
+ | procedure SLoadStringsFromFile(cfg: TJsonConfig; const path: String; List: TStrings); | ||
+ | var | ||
+ | i: Integer; | ||
+ | s: UnicodeString; | ||
+ | begin | ||
+ | List.Clear; | ||
+ | for i:= 0 to OptMaxHistoryItems-1 do | ||
+ | begin | ||
+ | s:= cfg.GetValue(path+'/'+inttostr(i), ''); | ||
+ | if s='' then | ||
+ | Break; | ||
+ | List.Add(Utf8Encode(s)); | ||
+ | end; | ||
+ | end; | ||
+ | |||
+ | procedure SSaveStringsToFile(cfg: TJsonConfig; const path: String; List: TStrings); | ||
+ | var | ||
+ | i: Integer; | ||
+ | s: String; | ||
+ | begin | ||
+ | for i:= 0 to OptMaxHistoryItems-1 do | ||
+ | begin | ||
+ | if i<List.Count then | ||
+ | s:= List[i] | ||
+ | else | ||
+ | s:= ''; | ||
+ | cfg.SetDeleteValue(path+'/'+inttostr(i), Utf8Decode(s), ''); | ||
+ | end; | ||
+ | end; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == From JsonViewer == | ||
+ | |||
Example usage can be found in the Lazarus jsonviewer tool (located in lazarus/tools/jsonviewer). | Example usage can be found in the Lazarus jsonviewer tool (located in lazarus/tools/jsonviewer). | ||
− | |||
In particular, this part of the tool shows how to use json: | In particular, this part of the tool shows how to use json: | ||
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
procedure TMainForm.OpenFile(Const AFileName : String); | procedure TMainForm.OpenFile(Const AFileName : String); | ||
− | + | var | |
− | |||
S : TFileStream; | S : TFileStream; | ||
P : TJSONParser; | P : TJSONParser; | ||
Line 47: | Line 233: | ||
procedure TMainForm.ShowJSONDocument; | procedure TMainForm.ShowJSONDocument; | ||
− | |||
begin | begin | ||
− | + | with TVJSON.Items do | |
− | + | begin | |
BeginUpdate; | BeginUpdate; | ||
try | try | ||
TVJSON.Items.Clear; | TVJSON.Items.Clear; | ||
SHowJSONData(Nil,FRoot); | SHowJSONData(Nil,FRoot); | ||
− | + | with TVJSON do | |
− | + | if (Items.Count>0) and Assigned(Items[0]) then | |
− | + | begin | |
Items[0].Expand(False); | Items[0].Expand(False); | ||
Selected:=Items[0]; | Selected:=Items[0]; | ||
− | + | end; | |
finally | finally | ||
EndUpdate; | EndUpdate; | ||
end; | end; | ||
− | + | end; | |
end; | end; | ||
procedure TMainForm.ShowJSONData(AParent : TTreeNode; Data : TJSONData); | procedure TMainForm.ShowJSONData(AParent : TTreeNode; Data : TJSONData); | ||
− | + | var | |
− | |||
N,N2 : TTreeNode; | N,N2 : TTreeNode; | ||
I : Integer; | I : Integer; | ||
Line 75: | Line 259: | ||
C : String; | C : String; | ||
S : TStringList; | S : TStringList; | ||
− | |||
begin | begin | ||
N:=Nil; | N:=Nil; | ||
if Assigned(Data) then | if Assigned(Data) then | ||
− | + | begin | |
− | + | case Data.JSONType of | |
jtArray, | jtArray, | ||
jtObject: | jtObject: | ||
begin | begin | ||
− | + | if (Data.JSONType=jtArray) then | |
− | + | C:=SArray | |
− | + | else | |
− | + | C:=SObject; | |
− | + | N:=TVJSON.Items.AddChild(AParent,Format(C,[Data.Count])); | |
− | + | S:=TstringList.Create; | |
− | + | try | |
− | + | for I:=0 to Data.Count-1 do | |
− | + | if Data.JSONtype=jtArray then | |
− | + | S.AddObject(IntToStr(I),Data.items[i]) | |
− | + | else | |
− | + | S.AddObject(TJSONObject(Data).Names[i],Data.items[i]); | |
− | + | if FSortObjectMembers and (Data.JSONType=jtObject) then | |
− | + | S.Sort; | |
− | + | for I:=0 to S.Count-1 do | |
begin | begin | ||
− | + | N2:=TVJSON.Items.AddChild(N,S[i]); | |
− | + | D:=TJSONData(S.Objects[i]); | |
− | + | N2.ImageIndex:=ImageTypeMap[D.JSONType]; | |
− | + | N2.SelectedIndex:=ImageTypeMap[D.JSONType]; | |
− | + | ShowJSONData(N2,D); | |
end | end | ||
− | + | finally | |
− | + | S.Free; | |
− | + | end; | |
end; | end; | ||
jtNull: | jtNull: | ||
N:=TVJSON.Items.AddChild(AParent,SNull); | N:=TVJSON.Items.AddChild(AParent,SNull); | ||
− | + | else | |
− | + | N:=TVJSON.Items.AddChild(AParent,Data.AsString); | |
end; | end; | ||
− | + | if Assigned(N) then | |
− | + | begin | |
N.ImageIndex:=ImageTypeMap[Data.JSONType]; | N.ImageIndex:=ImageTypeMap[Data.JSONType]; | ||
N.SelectedIndex:=ImageTypeMap[Data.JSONType]; | N.SelectedIndex:=ImageTypeMap[Data.JSONType]; | ||
N.Data:=Data; | N.Data:=Data; | ||
− | |||
end; | end; | ||
+ | end; | ||
end; | end; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | == FpcTwit == | |
+ | |||
+ | The [[Components_and_Code_examples#Networking|Components and Code examples]] library makes use of JSON to send/receive data. | ||
== See also == | == See also == | ||
− | |||
− | |||
− | |||
− | + | * FCL Reference for unit fpjson https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html | |
− | + | * An article covering use of XML and JSON in FreePascal: [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf PDF] | |
− | [ | + | * [[Package List]] |
− | [[ |
Revision as of 12:42, 31 August 2020
│
English (en) │
polski (pl) │
русский (ru) │
中文(中国大陆) (zh_CN) │
Info
fcl-json is a JSON implementation.
It contains units such as:
- fpjson: base unit which implements TJsonData and its children, e.g. TJsonObject
- jsonParser: implements TJsonParser, used in the From JsonViewer example below
- jsonConf: implements TJsonConfig which is handy to read/write application data from/to files
- jsonScanner: json source lexical analyzer
Note: In fpjson, accessing e.g. SomeJSONObject.Integers['price'] may give a SIGSEGV/Access Violation if that integer variable does not exist. This is apparently intentional, see [1]. You'd have to use the Find method (available since FPC 2.6.2) to first check if the element ('price' in this example) exists.
Streaming
fcl-json contains the unit "fpjsonrtti" which is used to load objects (TObject instances) from or save them to JSON format.
See Streaming JSON for a short example.
Examples
Getting Started
uses
fpjson, jsonparser;
procedure JSONTest;
var
jData : TJSONData;
jObject : TJSONObject;
jArray : TJSONArray;
s : String;
begin
// this is only a minimal sampling of what can be done with this API
// create from string
jData := GetJSON('{"Fld1" : "Hello", "Fld2" : 42, "Colors" : ["Red", "Green", "Blue"]}');
// output as a flat string
s := jData.AsJSON;
// output as nicely formatted JSON
s := jData.FormatJSON;
// cast as TJSONObject to make access easier
jObject := TJSONObject(jData);
// retrieve value of Fld1
s := jObject.Get('Fld1');
// change value of Fld2
jObject.Integers['Fld2'] := 123;
// retrieve the second color
s := jData.FindPath('Colors[1]').AsString;
// add a new element
jObject.Add('Happy', True);
// add a new sub-array
jArray := TJSONArray.Create;
jArray.Add('North');
jArray.Add('South');
jArray.Add('East');
jArray.Add('West');
jObject.Add('Directions', jArray);
end;
Note : its necessary to free jData when you have finished with it. Otherwise a memory leak is created !
Traversing Items
uses
Classes, TypInfo, fpjson, jsonparser;
procedure JSONItems(Info: TStrings);
var
jData : TJSONData;
jItem : TJSONData;
i, j: Integer;
object_name, field_name, field_value, object_type, object_items: String;
begin
jData := GetJSON('{"A":{"field1":0, "field2": false},"B":{"field1":0, "field2": false}}');
for i := 0 to jData.Count - 1 do
begin
jItem := jData.Items[i];
object_type := GetEnumName(TypeInfo(TJSONtype), Ord(jItem.JSONType));
object_name := TJSONObject(jData).Names[i];
WriteStr(object_items, jItem.Count);
Info.Append('object type: ' + object_type + '|object name: ' + object_name + '|number of fields: ' + object_items);
for j := 0 to jItem.Count - 1 do
begin
field_name := TJSONObject(jItem).Names[j];
field_value := jItem.FindPath(TJSONObject(jItem).Names[j]).AsString;
Info.Append(field_name + '|' + field_value);
end;
end;
jData.Free;
end;
Save/load dialog position/size
uses
jsonConf;
procedure TfmMain.SaveOptionsPos;
var
c: TJSONConfig;
begin
c:= TJSONConfig.Create(Nil);
try
c.Filename:= GetAppPath(cFileHistory);
c.SetValue('/dialog/max', WindowState=wsMaximized);
if WindowState<>wsMaximized then
begin
c.SetValue('/dialog/posx', Left);
c.SetValue('/dialog/posy', Top);
c.SetValue('/dialog/sizex', Width);
c.SetValue('/dialog/sizey', Height);
end;
finally
c.Free;
end;
end;
procedure TfmMain.LoadOptionsPos;
var
nLeft, nTop, nW, nH: Integer;
c: TJSONConfig;
begin
c:= TJSONConfig.Create(Nil);
try
c.Filename:= GetAppPath(cFileHistory);
nLeft:= c.GetValue('/dialog/posx', Left);
nTop:= c.GetValue('/dialog/posy', Top);
nW:= c.GetValue('/dialog/sizex', Width);
nH:= c.GetValue('/dialog/sizey', Height);
SetBounds(nLeft, nTop, nW, nH);
if c.GetValue('/dialog/max', false) then
WindowState:= wsMaximized;
finally
c.Free;
end;
end;
Save/load TStringList
//Example of path: '/list_find'
procedure SLoadStringsFromFile(cfg: TJsonConfig; const path: String; List: TStrings);
var
i: Integer;
s: UnicodeString;
begin
List.Clear;
for i:= 0 to OptMaxHistoryItems-1 do
begin
s:= cfg.GetValue(path+'/'+inttostr(i), '');
if s='' then
Break;
List.Add(Utf8Encode(s));
end;
end;
procedure SSaveStringsToFile(cfg: TJsonConfig; const path: String; List: TStrings);
var
i: Integer;
s: String;
begin
for i:= 0 to OptMaxHistoryItems-1 do
begin
if i<List.Count then
s:= List[i]
else
s:= '';
cfg.SetDeleteValue(path+'/'+inttostr(i), Utf8Decode(s), '');
end;
end;
From JsonViewer
Example usage can be found in the Lazarus jsonviewer tool (located in lazarus/tools/jsonviewer). In particular, this part of the tool shows how to use json:
procedure TMainForm.OpenFile(Const AFileName : String);
var
S : TFileStream;
P : TJSONParser;
D : TJSONData;
begin
S:=TFileStream.Create(AFileName,fmOpenRead);
try
P:=TJSONParser.Create(S);
try
P.Strict:=FStrict;
D:=P.Parse;
finally
P.Free;
end;
finally
S.Free;
end;
FFileName:=AFileName;
SetCaption;
FreeAndNil(FRoot);
FRoot:=D;
ShowJSONDocument;
end;
procedure TMainForm.ShowJSONDocument;
begin
with TVJSON.Items do
begin
BeginUpdate;
try
TVJSON.Items.Clear;
SHowJSONData(Nil,FRoot);
with TVJSON do
if (Items.Count>0) and Assigned(Items[0]) then
begin
Items[0].Expand(False);
Selected:=Items[0];
end;
finally
EndUpdate;
end;
end;
end;
procedure TMainForm.ShowJSONData(AParent : TTreeNode; Data : TJSONData);
var
N,N2 : TTreeNode;
I : Integer;
D : TJSONData;
C : String;
S : TStringList;
begin
N:=Nil;
if Assigned(Data) then
begin
case Data.JSONType of
jtArray,
jtObject:
begin
if (Data.JSONType=jtArray) then
C:=SArray
else
C:=SObject;
N:=TVJSON.Items.AddChild(AParent,Format(C,[Data.Count]));
S:=TstringList.Create;
try
for I:=0 to Data.Count-1 do
if Data.JSONtype=jtArray then
S.AddObject(IntToStr(I),Data.items[i])
else
S.AddObject(TJSONObject(Data).Names[i],Data.items[i]);
if FSortObjectMembers and (Data.JSONType=jtObject) then
S.Sort;
for I:=0 to S.Count-1 do
begin
N2:=TVJSON.Items.AddChild(N,S[i]);
D:=TJSONData(S.Objects[i]);
N2.ImageIndex:=ImageTypeMap[D.JSONType];
N2.SelectedIndex:=ImageTypeMap[D.JSONType];
ShowJSONData(N2,D);
end
finally
S.Free;
end;
end;
jtNull:
N:=TVJSON.Items.AddChild(AParent,SNull);
else
N:=TVJSON.Items.AddChild(AParent,Data.AsString);
end;
if Assigned(N) then
begin
N.ImageIndex:=ImageTypeMap[Data.JSONType];
N.SelectedIndex:=ImageTypeMap[Data.JSONType];
N.Data:=Data;
end;
end;
end;
FpcTwit
The Components and Code examples library makes use of JSON to send/receive data.
See also
- FCL Reference for unit fpjson https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html
- An article covering use of XML and JSON in FreePascal: PDF
- Package List