Difference between revisions of "fcl-json"

From Lazarus wiki
Jump to navigationJump to search
(Units)
(Added link to FCL page)
(24 intermediate revisions by 8 users not shown)
Line 1: Line 1:
fcl-json - a [[JSON]] (Javascript Object Notation) implementation
+
{{fcl-json}}
  
== Info ==
+
= Info =
Contains such units:
+
 
 +
fcl-json is a [[JSON]] implementation.
 +
 
 +
It contains units such as:
  
 
* fpjson: base unit which implements TJsonData and its children, e.g. TJsonObject
 
* fpjson: base unit which implements TJsonData and its children, e.g. TJsonObject
* jsonParser: implements TJsonParser used in example below
+
* jsonParser: implements TJsonParser, used in the [[#From_JsonViewer|From JsonViewer]] example below
* jsonConf: implements TJsonConfig which is handy to read/write application data to files
+
* jsonConf: implements TJsonConfig which is handy to read/write application data from/to files
 
* jsonScanner: json source lexical analyzer
 
* jsonScanner: json source lexical analyzer
  
Line 12: Line 15:
 
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 ==
+
= 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 ==
+
= 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
Var
 
 
   S : TFileStream;
 
   S : TFileStream;
 
   P : TJSONParser;
 
   P : TJSONParser;
Line 50: Line 233:
  
 
procedure TMainForm.ShowJSONDocument;
 
procedure TMainForm.ShowJSONDocument;
 
 
begin
 
begin
   With TVJSON.Items do
+
   with TVJSON.Items do
    begin
+
  begin
 
     BeginUpdate;
 
     BeginUpdate;
 
     try
 
     try
 
       TVJSON.Items.Clear;
 
       TVJSON.Items.Clear;
 
       SHowJSONData(Nil,FRoot);
 
       SHowJSONData(Nil,FRoot);
       With TVJSON do
+
       with TVJSON do
         If (Items.Count>0) and Assigned(Items[0]) then
+
         if (Items.Count>0) and Assigned(Items[0]) then
          begin
+
        begin
 
           Items[0].Expand(False);
 
           Items[0].Expand(False);
 
           Selected:=Items[0];
 
           Selected:=Items[0];
          end;
+
        end;
 
     finally
 
     finally
 
       EndUpdate;
 
       EndUpdate;
 
     end;
 
     end;
    end;
+
  end;
 
end;
 
end;
  
 
procedure TMainForm.ShowJSONData(AParent : TTreeNode; Data : TJSONData);
 
procedure TMainForm.ShowJSONData(AParent : TTreeNode; Data : TJSONData);
 
+
var
Var
 
 
   N,N2 : TTreeNode;
 
   N,N2 : TTreeNode;
 
   I : Integer;
 
   I : Integer;
Line 78: 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
+
  begin
     Case Data.JSONType of
+
     case Data.JSONType of
 
       jtArray,
 
       jtArray,
 
       jtObject:
 
       jtObject:
 
         begin
 
         begin
        If (Data.JSONType=jtArray) then
+
          if (Data.JSONType=jtArray) then
          C:=SArray
+
            C:=SArray
        else
+
          else
          C:=SObject;
+
            C:=SObject;
        N:=TVJSON.Items.AddChild(AParent,Format(C,[Data.Count]));
+
          N:=TVJSON.Items.AddChild(AParent,Format(C,[Data.Count]));
        S:=TstringList.Create;
+
          S:=TstringList.Create;
        try
+
          try
          For I:=0 to Data.Count-1 do
+
            for I:=0 to Data.Count-1 do
            If Data.JSONtype=jtArray then
+
              if Data.JSONtype=jtArray then
              S.AddObject(IntToStr(I),Data.items[i])
+
                S.AddObject(IntToStr(I),Data.items[i])
            else
+
              else
              S.AddObject(TJSONObject(Data).Names[i],Data.items[i]);
+
                S.AddObject(TJSONObject(Data).Names[i],Data.items[i]);
          If FSortObjectMembers and (Data.JSONType=jtObject) then
+
            if FSortObjectMembers and (Data.JSONType=jtObject) then
            S.Sort;
+
              S.Sort;
          For I:=0 to S.Count-1 do
+
            for I:=0 to S.Count-1 do
 
             begin
 
             begin
            N2:=TVJSON.Items.AddChild(N,S[i]);
+
              N2:=TVJSON.Items.AddChild(N,S[i]);
            D:=TJSONData(S.Objects[i]);
+
              D:=TJSONData(S.Objects[i]);
            N2.ImageIndex:=ImageTypeMap[D.JSONType];
+
              N2.ImageIndex:=ImageTypeMap[D.JSONType];
            N2.SelectedIndex:=ImageTypeMap[D.JSONType];
+
              N2.SelectedIndex:=ImageTypeMap[D.JSONType];
            ShowJSONData(N2,D);
+
              ShowJSONData(N2,D);
 
             end
 
             end
        finally
+
          finally
          S.Free;
+
            S.Free;
        end;
+
          end;
 
         end;
 
         end;
 
       jtNull:
 
       jtNull:
 
         N:=TVJSON.Items.AddChild(AParent,SNull);
 
         N:=TVJSON.Items.AddChild(AParent,SNull);
    else
+
      else
      N:=TVJSON.Items.AddChild(AParent,Data.AsString);
+
        N:=TVJSON.Items.AddChild(AParent,Data.AsString);
 
     end;
 
     end;
     If Assigned(N) then
+
     if Assigned(N) then
      begin
+
    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;   
 
end;   
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Also, the [[FPC Applications/Projects Gallery#FPCTwit|fpctwit]] library makes use of JSON to send/receive data.
+
== FpcTwit ==
 +
 
 +
The [[Components_and_Code_examples#Networking|Components and Code examples]] library makes use of JSON to send/receive data.
  
 
== See also ==
 
== See also ==
An article covering use of XML and JSON in FreePascal: [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf PDF]
 
 
[[Package List]]
 
  
[[Category:Packages]]
+
* FCL Reference for unit fpjson https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html
[[Category:Free Component Library]]
+
* An article covering use of XML and JSON in FreePascal: [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf PDF]
[[Category:JSON]]
+
* [[Package List]]
[[Category:FPC]]
 

Revision as of 11: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