Difference between revisions of "fcl-json"

From Free Pascal wiki
Jump to navigationJump to search
(Sample Usage)
 
(40 intermediate revisions by 8 users not shown)
Line 1: Line 1:
= Info =
+
{{fcl-json}}
  
fcl-json is [[JSON]] implementation.
+
= General 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
  
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 20: Line 26:
 
= Examples =
 
= Examples =
  
== Save/load dialog position/size ==
 
  
<syntaxhighlight>
+
== Getting Started ==
uses jsonConf;
+
 
 +
<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 := 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;
 +
 
 +
</syntaxhighlight>
 +
 
 +
Note: it's 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 form position to/from file==
 +
 
 +
<syntaxhighlight lang=pascal>
 +
uses
 +
  jsonConf;
  
procedure TfmMain.SaveOptionsPos;
+
procedure SaveFormPos(AForm: TForm; const AFilename: string);
 
var
 
var
 
   c: TJSONConfig;
 
   c: TJSONConfig;
 
begin
 
begin
   c:= TJSONConfig.Create(nil);
+
   c:= TJSONConfig.Create(Nil);
 
   try
 
   try
     c.Filename:= GetAppPath(cFileHistory);
+
     //try/except to handle broken json file
     c.SetValue('/dialog/max', WindowState=wsMaximized);
+
    try
     if WindowState<>wsMaximized then
+
      c.Formatted:= true;
 +
      c.Filename:= AFilename;
 +
    except
 +
      exit;
 +
    end;
 +
 
 +
     c.SetValue('/dialog/max', AForm.WindowState=wsMaximized);
 +
     if AForm.WindowState<>wsMaximized then
 
     begin
 
     begin
       c.SetValue('/dialog/posx', Left);
+
       c.SetValue('/dialog/posx', AForm.Left);
       c.SetValue('/dialog/posy', Top);
+
       c.SetValue('/dialog/posy', AForm.Top);
       c.SetValue('/dialog/sizex', Width);
+
       c.SetValue('/dialog/sizex', AForm.Width);
       c.SetValue('/dialog/sizey', Height);
+
       c.SetValue('/dialog/sizey', AForm.Height);
 
     end;
 
     end;
 
   finally
 
   finally
Line 45: Line 151:
 
end;
 
end;
  
procedure TfmMain.LoadOptionsPos;
+
procedure LoadFormPos(AForm: TForm; const AFilename: string);
 
var
 
var
   nLeft, nTop, nW, nH: integer;
+
   nLeft, nTop, nW, nH: Integer;
 
   c: TJSONConfig;
 
   c: TJSONConfig;
 
begin
 
begin
   c:= TJSONConfig.Create(nil);
+
   c:= TJSONConfig.Create(Nil);
 
   try
 
   try
     c.Filename:= GetAppPath(cFileHistory);
+
     //try/except to handle broken json file
 +
    try
 +
      c.Formatted:= true;
 +
      c.Filename:= AFilename;
 +
    except
 +
      exit;
 +
    end;
  
     nLeft:= c.GetValue('/dialog/posx', Left);
+
     nLeft:= c.GetValue('/dialog/posx', AForm.Left);
     nTop:= c.GetValue('/dialog/posy', Top);
+
     nTop:= c.GetValue('/dialog/posy', AForm.Top);
     nW:= c.GetValue('/dialog/sizex', Width);
+
     nW:= c.GetValue('/dialog/sizex', AForm.Width);
     nH:= c.GetValue('/dialog/sizey', Height);
+
     nH:= c.GetValue('/dialog/sizey', AForm.Height);
     SetBounds(nLeft, nTop, nW, nH);
+
     AForm.SetBounds(nLeft, nTop, nW, nH);
  
 
     if c.GetValue('/dialog/max', false) then
 
     if c.GetValue('/dialog/max', false) then
       WindowState:= wsMaximized;
+
       AForm.WindowState:= wsMaximized;
 
   finally
 
   finally
 
     c.Free;
 
     c.Free;
Line 70: Line 182:
 
== Save/load TStringList ==
 
== Save/load TStringList ==
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
//Example of path: '/list_find'
+
uses
 
+
  jsonConf;
procedure SLoadStringsFromFile(cfg: TJsonConfig; const path: string; List: TStrings);
 
 
var
 
var
   i: integer;
+
   cfg: TJSONConfig;
   s: UnicodeString;
+
   List: TStringList;
 +
  path: string;
 
begin
 
begin
   List.Clear;
+
   List:= TStringList.Create;
   for i:= 0 to OptMaxHistoryItems-1 do
+
   cfg:= TJSONConfig.Create(nil);
   begin
+
   try
     s:= cfg.GetValue(path+'/'+inttostr(i), '');
+
    //try/except to handle broken json file
    if s='' then Break;
+
     try
     List.Add(Utf8Encode(s));
+
      cfg.Formatted:= true;
  end;
+
      cfg.Filename:= AJsonFilename;
end;
+
     except
 +
      exit;
 +
    end;
  
procedure SSaveStringsToFile(cfg: TJsonConfig; const path: string; List: TStrings);
+
    cfg.GetValue('/mylist', List, '');
var
+
     List.Add('some_value');
  i: integer;
+
     cfg.SetValue('/mylist', List);
  s: string;
+
   finally
begin
+
    cfg.Free;
  for i:= 0 to OptMaxHistoryItems-1 do
+
    List.Free;
  begin
+
  end;
     if i<List.Count then
 
      s:= List[i]
 
    else
 
      s:= '';
 
     cfg.SetDeleteValue(path+'/'+inttostr(i), Utf8Decode(s), '');
 
   end;
 
end;
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 108: Line 215:
 
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 136: Line 242:
  
 
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 164: Line 268:
 
   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>
  
== Sample Usage ==
+
==Change formatting of float numbers==
  
<syntaxhighlight>
+
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:
  
procedure JSONTest;
+
"coordinates" : [
var
+
      5.5978631048365003E+001,
  jData : TJSONData;
+
      2.2100000000000000E+002
  jObject : TJSONObject;
+
]
  s : string;
 
begin
 
  
  // create from string
+
I want to see normal form:
  jData := GetJSON('{"Fld1" : "Hello", "Fld2" : 42, "Colors" : ["Red", "Green", "Blue"]}');
 
  
  // output as a flat string
+
"coordinates" : [
  s := jData.AsJSON;
+
      55.978631048365003,
 +
      221.0
 +
]
  
  //  output as nicely formatted JSON
+
A (forum members '''y.ivanov''', '''rvk''', '''wp'''):
  s := jData.FormatJSON;
+
<syntaxhighlight lang="pascal">
 
+
{$mode objfpc}{$h+}
  // cast as TJSONObject to make access easier
+
uses
  jObject := TJSONObject(jData);
+
  fpjson,
 
+
  jsonparser,
  // retrieve value of Fld1
+
  SysUtils;
  s := jObject.Get('Fld1');
+
 
+
type
  // change value of Fld2
+
  TJSONFloat4Number = class(TJSONFloatNumber)
  jObject.Integers['Fld2'] := 123;
+
  protected
 
+
    function GetAsString: TJSONStringType; override;
  // retrieve the second color
+
  end;
  s := jData.FindPath('Colors[1]').AsString;
+
 +
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;
 
end;
 
+
 +
begin
 +
  SetJSONInstanceType(jitNumberFloat, TJSONFloat4Number);
 +
  JSONTest;
 +
  Readln;
 +
end.
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== FpcTwit ==
+
== See also ==
 
 
The [[FPC Applications/Projects Gallery#FPCTwit|fpctwit]] library makes use of JSON to send/receive data.
 
 
 
= 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:FPC]]
+
* The FpcTwit example in the [[Components_and_Code_examples#Networking|Components and Code examples]] library makes use of JSON to send/receive data.
[[Category:FCL]]
+
* [https://lazarus-ccr.sourceforge.io/fpcdoc/fcl/fpjson/index.html FCL Reference] for unit fpjson.
[[Category:Packages]]
+
* [http://www.freepascal.org/~michael/articles/webdata/webdata.pdf An article (PDF)] covering use of XML and JSON in FreePascal.
[[Category:JSON]]
+
* [[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