Difference between revisions of "fcl-json"

From Free Pascal wiki
Jump to navigationJump to search
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.
  

Revision as of 15:43, 6 October 2022

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 := 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 by forum member y.ivanov: If you want just 4 digits of precision:

uses
  fpjson;
 
type
  TJSONFloat4Number = class(TJSONFloatNumber)
  protected
    function GetAsString: TJSONStringType; override;
  end;
 
function TJSONFloat4Number.GetAsString: TJSONStringType;
var
  F: TJSONFloat;
begin
  F := GetAsFloat;
  Str(F:0:4, Result); // format with your preferences
end;

Later:

  ...
  SetJSONInstanceType(jitNumberFloat, TJSONFloat4Number);
  S := JsonData.AsJSON;

I'm afraid that SetJSONInstanceType works globally.

See also