Difference between revisions of "fcl-json"

From Free Pascal wiki
Jump to navigationJump to search
 
(12 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
{{fcl-json}}
 
{{fcl-json}}
  
= Info =
+
= General info =
  
 
fcl-json is a [[JSON]] implementation.  
 
fcl-json is a [[JSON]] implementation.  
Line 12: Line 12:
 
* jsonScanner: json source lexical analyzer
 
* 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.
 +
<syntaxhighlight lang="pascal">
 +
SomeJSONObject.Integers['price']
 +
</syntaxhighlight>
 +
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.
  
Line 114: Line 118:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Save/load dialog position/size ==
+
== Save/load form position to/from file==
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 313: Line 317:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Changing formatting of float numbers==
+
==Change formatting of float numbers==
  
 
Q: My program generates and writes data to JSON file. I use FormatJSON() method to make output more readable. I'm not quite satisfied how numbers with float point look:
 
Q: My program generates and writes data to JSON file. I use FormatJSON() method to make output more readable. I'm not quite satisfied how numbers with float point look:
Line 329: Line 333:
 
  ]
 
  ]
  
A: If you want just 4 digits of precision:
+
A (forum members '''y.ivanov''', '''rvk''', '''wp'''):
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 +
{$mode objfpc}{$h+}
 
uses
 
uses
   fpjson;
+
   fpjson,
 +
  jsonparser,
 +
  SysUtils;
 
   
 
   
 
type
 
type
Line 343: Line 350:
 
var
 
var
 
   F: TJSONFloat;
 
   F: TJSONFloat;
 +
  fs: TFormatSettings;
 
begin
 
begin
 +
  fs := DefaultFormatSettings;
 +
  fs.DecimalSeparator := '.';
 
   F := GetAsFloat;
 
   F := GetAsFloat;
   Str(F:0:4, Result); // format with your preferences
+
   Result := FormatFloat('0.0###############', F, fs); // format with your preferences
 
end;
 
end;
</syntaxhighlight>
 
  
Later:
+
procedure JSONTest;
<syntaxhighlight lang="pascal">
+
var
   ...
+
  jData: TJSONData;
 +
begin
 +
  jData := GetJSON('{"coordinates": [5.5978631048365003E+001, 2.2100000000000000E+002]}');
 +
   writeln(jData.FormatJSON);
 +
  jData.Free;
 +
end;
 +
 +
begin
 
   SetJSONInstanceType(jitNumberFloat, TJSONFloat4Number);
 
   SetJSONInstanceType(jitNumberFloat, TJSONFloat4Number);
   S := JsonData.AsJSON;
+
   JSONTest;
 +
  Readln;
 +
end.
 
</syntaxhighlight>
 
</syntaxhighlight>
 
I'm afraid that SetJSONInstanceType works globally.
 
  
 
== See also ==
 
== See also ==
  
 
* The FpcTwit example in the [[Components_and_Code_examples#Networking|Components and Code examples]] library makes use of JSON to send/receive data.
 
* The FpcTwit example in the [[Components_and_Code_examples#Networking|Components and Code examples]] library makes use of JSON to send/receive data.
* FCL Reference for unit fpjson https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html
+
* [https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html FCL Reference] for unit fpjson.
* An article covering use of XML and JSON in FreePascal: [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf PDF]
+
* [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf An article (PDF)] covering use of XML and JSON in FreePascal.
 
* [[Package List]]
 
* [[Package List]]

Latest revision as of 19:09, 6 October 2022

English (en) polski (pl) русский (ru) 中文(中国大陆)‎ (zh_CN)

General 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 := jData as TJSONObject;

  // 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: it's 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 form position to/from file

uses
  jsonConf;

procedure SaveFormPos(AForm: TForm; const AFilename: string);
var
  c: TJSONConfig;
begin
  c:= TJSONConfig.Create(Nil);
  try
    //try/except to handle broken json file
    try
      c.Formatted:= true;
      c.Filename:= AFilename;
    except
      exit;
    end;

    c.SetValue('/dialog/max', AForm.WindowState=wsMaximized);
    if AForm.WindowState<>wsMaximized then
    begin
      c.SetValue('/dialog/posx', AForm.Left);
      c.SetValue('/dialog/posy', AForm.Top);
      c.SetValue('/dialog/sizex', AForm.Width);
      c.SetValue('/dialog/sizey', AForm.Height);
    end;
  finally
    c.Free;
  end;
end;

procedure LoadFormPos(AForm: TForm; const AFilename: string);
var
  nLeft, nTop, nW, nH: Integer;
  c: TJSONConfig;
begin
  c:= TJSONConfig.Create(Nil);
  try
    //try/except to handle broken json file
    try
      c.Formatted:= true;
      c.Filename:= AFilename;
    except
      exit;
    end;

    nLeft:= c.GetValue('/dialog/posx', AForm.Left);
    nTop:= c.GetValue('/dialog/posy', AForm.Top);
    nW:= c.GetValue('/dialog/sizex', AForm.Width);
    nH:= c.GetValue('/dialog/sizey', AForm.Height);
    AForm.SetBounds(nLeft, nTop, nW, nH);

    if c.GetValue('/dialog/max', false) then
      AForm.WindowState:= wsMaximized;
  finally
    c.Free;
  end;
end;

Save/load TStringList

uses
  jsonConf;
var
  cfg: TJSONConfig;
  List: TStringList;
  path: string;
begin
  List:= TStringList.Create;
  cfg:= TJSONConfig.Create(nil);
  try
    //try/except to handle broken json file
    try
      cfg.Formatted:= true;
      cfg.Filename:= AJsonFilename;
    except
      exit;
    end;

    cfg.GetValue('/mylist', List, '');
    List.Add('some_value');
    cfg.SetValue('/mylist', List);
  finally
    cfg.Free;
    List.Free;
  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;

Change formatting of float numbers

Q: My program generates and writes data to JSON file. I use FormatJSON() method to make output more readable. I'm not quite satisfied how numbers with float point look:

"coordinates" : [
     5.5978631048365003E+001,
     2.2100000000000000E+002
]

I want to see normal form:

"coordinates" : [
     55.978631048365003,
     221.0
]

A (forum members y.ivanov, rvk, wp):

{$mode objfpc}{$h+}
uses
  fpjson,
  jsonparser,
  SysUtils;
 
type
  TJSONFloat4Number = class(TJSONFloatNumber)
  protected
    function GetAsString: TJSONStringType; override;
  end;
 
function TJSONFloat4Number.GetAsString: TJSONStringType;
var
  F: TJSONFloat;
  fs: TFormatSettings;
begin
  fs := DefaultFormatSettings;
  fs.DecimalSeparator := '.';
  F := GetAsFloat;
  Result := FormatFloat('0.0###############', F, fs); // format with your preferences
end;

procedure JSONTest;
var
  jData: TJSONData;
begin
  jData := GetJSON('{"coordinates": [5.5978631048365003E+001, 2.2100000000000000E+002]}');
  writeln(jData.FormatJSON);
  jData.Free;
end;
 
begin
  SetJSONInstanceType(jitNumberFloat, TJSONFloat4Number);
  JSONTest;
  Readln;
end.

See also