Recent

Author Topic: Trying to parse some JSON files  (Read 12648 times)

blackspaceghost

  • New Member
  • *
  • Posts: 14
Trying to parse some JSON files
« on: August 10, 2021, 08:25:59 am »
Hello all, i tried to parse some JSON files and if some keys are the same in files - copy first value of key in to second, and i have achieved some results, but, actually, when i try to parse some jtArray type in a nested values (objects), i have problems with it. And i dont understand why, because all is worked, except the jtArrays.  In not nested values it works fine. Hope for your help guys!!!! trying to solve it for a week already :(
here the code and 2 JSON Objects:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.  {$IFDEF UNIX}
  7.   cthreads,
  8.                         {$ENDIF}
  9.   Classes,
  10.   SysUtils,
  11.   CustApp { you can add units after this },
  12.   jsonscanner,
  13.   fpjson,
  14.   jsonparser;
  15.  
  16. type
  17.  
  18.   { TMyTimeTest }
  19.  
  20.   TMyTimeTest = class(TCustomApplication)
  21.   private
  22.     fFileData: string;
  23.     fPath1: string;
  24.     fPath2: string;
  25.     fFlag: boolean;
  26.     fStr1: string;
  27.     fStr2: string;
  28.  
  29.     procedure SetPath1(path: string);
  30.     procedure SetPath2(path: string);
  31.     procedure SetFlag(flag: boolean);
  32.     procedure SetStr1(str1: string);
  33.     procedure SetStr2(str2: string);
  34.   protected
  35.     procedure DoRun; override;
  36.   public
  37.     JSONObject1, JSONObject2: TJSONObject;
  38.     JSONEnum1, JSONEnum2: TBaseJSONEnumerator;
  39.     constructor Create(TheOwner: TComponent); override;
  40.     destructor Destroy; override;
  41.     procedure WriteHelp; virtual;
  42.     procedure WriteInfo;
  43.     procedure SetFileData;
  44.     procedure ParseAndSet;
  45.     procedure WriteInFile;
  46.     property Path1: string read fPath1 write SetPath1;
  47.     property Flag: boolean read fFlag write SetFlag;
  48.     property Str1: string read fStr1 write SetStr1;
  49.     property Str2: string read fStr2 write SetStr2;
  50.   end;
  51.  
  52.   { TMyTimeTest }
  53.  
  54.   procedure TMyTimeTest.SetPath1(path: string);
  55.   begin
  56.     fPath1 := path;
  57.   end;
  58.  
  59.   procedure TMyTimeTest.SetPath2(path: string);
  60.   begin
  61.     fPath2 := path;
  62.   end;
  63.  
  64.   procedure TMyTimeTest.SetFlag(flag: boolean);
  65.   begin
  66.     fFlag := flag;
  67.   end;
  68.  
  69.   procedure TMyTimeTest.SetStr1(str1: string);
  70.   begin
  71.     fStr1 := str1;
  72.   end;
  73.  
  74.   procedure TMyTimeTest.SetStr2(str2: string);
  75.   begin
  76.     fStr2 := str2;
  77.   end;
  78.  
  79.   procedure TMyTimeTest.SetFileData;
  80.   var
  81.  
  82.     L: TStringList;
  83.  
  84.   begin
  85.  
  86.     //if str1 = '' then
  87.     //  fPath1 := ParamStr(1)
  88.     //else
  89.     //  fPath2 := ParamStr(2);
  90.     fPath1 := '/media/sf_Devices/u1.json';
  91.     fPath2 := '/media/sf_Devices/u2.json';
  92.     try
  93.       try
  94.         L := TStringList.Create;
  95.         L.LoadFromFile(fPath1);
  96.         if L <> nil then
  97.         begin
  98.           fFileData := L.Text;
  99.           Self.SetFlag(True);
  100.           if str1 = '' then
  101.             str1 := fFileData;
  102.         end;
  103.       finally
  104.         L.Free;
  105.       end;
  106.     except
  107.       ShowException(Exception.Create('1'));
  108.       Terminate;
  109.       Halt;
  110.     end;
  111.     //if str1 = '' then
  112.     //  fPath1 := ParamStr(1)
  113.     //else
  114.     //  fPath2 := ParamStr(2);
  115.     try
  116.       try
  117.         L := TStringList.Create;
  118.         L.LoadFromFile(fPath2);
  119.         if L <> nil then
  120.         begin
  121.           fFileData := L.Text;
  122.           Self.SetFlag(True);
  123.           if str2 = '' then
  124.             str2 := fFileData;
  125.         end;
  126.       finally
  127.         L.Free;
  128.       end;
  129.     except
  130.       ShowException(Exception.Create('1'));
  131.       Terminate;
  132.       Halt;
  133.     end;
  134.   end;
  135.  
  136.   procedure TMyTimeTest.ParseAndSet;
  137.   var
  138.     JSONParser1, JSONParser2: TJSONParser;
  139.     JNestObject1, JNestObject2: TJSONObject;
  140.     JSONNestEnum1, JSONNestEnum2, JSONArrayEnum: TBaseJSONEnumerator;
  141.     i: integer;
  142.   begin
  143.     JSONParser1 := TJSONParser.Create(str1, DefaultOptions);
  144.     JSONParser2 := TJSONParser.Create(str2, DefaultOptions);
  145.     try
  146.       JSONObject1 := JSONParser1.Parse as TJSONObject;
  147.       JSONObject2 := JSONParser2.Parse as TJSONObject;
  148.       try
  149.         JSONEnum1 := JSONObject1.GetEnumerator;
  150.         JSONEnum2 := JSONObject2.GetEnumerator;
  151.  
  152.         try
  153.          
  154.           while JSONEnum1.MoveNext do
  155.             while JSONEnum2.MoveNext do
  156.             begin
  157.               if JSONEnum1.Current.Value.JSONType = jtObject then
  158.                
  159.               begin
  160.                
  161.                 JNestObject1 := JSONEnum1.Current.Value as TJSONObject;
  162.                 JNestObject2 := JSONObject2.Find(JSONEnum1.Current.Key) as TJSONObject;
  163.                 try
  164.                   JSONNestEnum1 := JNestObject1.GetEnumerator;
  165.                   JSONNestEnum2 := JNestObject2.GetEnumerator;
  166.                   while JSONNestEnum1.MoveNext do
  167.                     while JSONNestEnum2.MoveNext do
  168.                     begin
  169.                       if JSONObject2.FindPath(JSONEnum1.Current.key + '.' + JSONNestEnum1.Current.Key) <> nil then
  170.                       begin
  171.                         if JSONNestEnum1.Current.Value.JSONType = jtString then
  172.                        
  173.                         begin
  174.                           JNestObject2.Strings[JSONNestEnum1.Current.Key] := JSONNestEnum1.Current.Value.AsString;
  175.                          
  176.                           Break;
  177.                          
  178.                         end
  179.                         else if JSONNestEnum1.Current.Value.JSONType = jtNumber then
  180.                          
  181.                         begin
  182.                           JNestObject2.Integers[JSONNestEnum1.Current.Key] := JSONNestEnum1.Current.Value.AsInt64;
  183.                           Break;
  184.                         end
  185.                         else if JSONNestEnum1.Current.Value.JSONType = jtBoolean then
  186.                          
  187.                         begin
  188.                           JNestObject2.Booleans[JSONNestEnum1.Current.Key] := JSONNestEnum1.Current.Value.AsBoolean;
  189.                          
  190.                           Break;
  191.  
  192.                         end
  193.                         else if JSONNestEnum1.Current.Value.JSONType = jtArray then
  194.                         begin
  195.                           JSONArrayEnum := TJSONArray(JSONNestEnum1.Current.Value).GetEnumerator;
  196.                           JSONObject2.Arrays[JSONNestEnum1.Current.Key].Clear;
  197.                           while JSONArrayEnum.MoveNext do
  198.                             JSONObject2.Arrays[JSONNestEnum1.Current.key].Add(JSONArrayEnum.Current.Value);
  199.                           //JSONObject2.Arrays[JSONNestEnum1.Current.Key] := (TJSONArray(JNestObject2.Items[2]));;
  200.                           //for i:=0 to TJSONArray(JNestObject2.Items[2]).Count-1 do
  201.  
  202.                         end;
  203.                         Break;
  204.                       end;
  205.                     end;
  206.                 finally
  207.                   FreeAndNil(JSONNestEnum1);
  208.                   FreeAndNil(JSONNestEnum2);
  209.                 end;
  210.               end
  211.               else if JSONObject2.FindPath(JSONEnum1.Current.Key) <> nil then
  212.               begin
  213.                 if JSONEnum1.Current.Value.JSONType = jtString then
  214.                  
  215.                 begin
  216.                   JSONObject2.Strings[JSONEnum1.Current.Key] := JSONEnum1.Current.Value.AsString;
  217.                  
  218.                   Break;
  219.                  
  220.                 end
  221.                 else if JSONEnum1.Current.Value.JSONType = jtNumber then
  222.                  
  223.                 begin
  224.                   JSONObject2.Integers[JSONEnum1.Current.Key] := JSONEnum1.Current.Value.AsInt64;
  225.                   Break;
  226.                 end
  227.                 else if JSONEnum1.Current.Value.JSONType = jtBoolean then
  228.                  
  229.                 begin
  230.                   JSONObject2.Booleans[JSONEnum1.Current.Key] := JSONEnum1.Current.Value.AsBoolean;
  231.                
  232.                   Break;
  233.                 end
  234.                 else if JSONEnum1.Current.Value.JSONType = jtArray then
  235.                 begin
  236.                   JSONArrayEnum := TJSONArray(JSONEnum1.Current.Value).GetEnumerator;
  237.                   JSONObject2.Arrays[JSONEnum1.Current.Key].Clear;
  238.                   while JSONArrayEnum.MoveNext do
  239.                     JSONObject2.Arrays[JSONEnum1.Current.key].Add(JSONArrayEnum.Current.Value);
  240.                   Break;
  241.                 end;
  242.                 Break;
  243.               end;
  244.               Break;
  245.             end;
  246.         finally
  247.           FreeAndNil(JsonEnum1);
  248.           FreeAndNil(JsonEnum2);
  249.  
  250.         end;
  251.       finally
  252.         //FreeAndNil(JsonObject1);
  253.         //FreeAndNil(JsonObject2);
  254.       end;
  255.  
  256.     finally
  257.       FreeAndNil(JsonParser1);
  258.       FreeAndNil(JsonParser2);
  259.     end;
  260.   end;
  261.  
  262.   procedure TMyTimeTest.WriteInFile;
  263.   var
  264.     L: TStringList;
  265.   begin
  266.     L := TStringList.Create;
  267.     try
  268.       L.Text := Trim(JSONObject2.FormatJSON);
  269.       L.SaveToFile(fPath2);
  270.       ShowException(Exception.Create('0'));
  271.     finally
  272.       L.Free;
  273.       JSONObject2.Free;
  274.     end;
  275.   end;
  276.  
  277.  
  278.   procedure TMyTimeTest.DoRun;
  279.   var
  280.     ErrorMsg: string;
  281.   begin
  282.  
  283.     // parse parameters
  284.     if HasOption('h', 'help') then
  285.     begin
  286.       WriteHelp;
  287.       Terminate;
  288.       Exit;
  289.     end;
  290.  
  291.     if HasOption('i', 'info') then
  292.     begin
  293.       WriteInfo;
  294.       Terminate;
  295.       Exit;
  296.     end;
  297.  
  298.     // quick check parameters
  299.     ErrorMsg := CheckOptions('h', 'help');
  300.     if ErrorMsg <> '' then
  301.     begin
  302.       ShowException(Exception.Create(ErrorMsg));
  303.      
  304.       Terminate;
  305.       Exit;
  306.     end;
  307.  
  308.  
  309.  
  310.     { add your program here }
  311.     Self.SetFileData;        
  312.     Self.SetFileData;        
  313.     if flag = True then
  314.     begin
  315.       Self.ParseAndSet;
  316.       Self.WriteInFile;
  317.     end;
  318.  
  319.     // stop program loop
  320.     Terminate;
  321.   end;
  322.  
  323.  
  324.  
  325.   constructor TMyTimeTest.Create(TheOwner: TComponent);
  326.   begin
  327.     inherited Create(TheOwner);
  328.     StopOnException := True;
  329.   end;
  330.  
  331.   destructor TMyTimeTest.Destroy;
  332.   begin
  333.     inherited Destroy;
  334.   end;
  335.  
  336.   procedure TMyTimeTest.WriteHelp;
  337.   begin
  338.     { add your help code here }
  339.    
  340.  
  341.   end;
  342.  
  343.   procedure TMyTimeTest.WriteInfo;
  344.   begin
  345.    
  346.   end;
  347.  
  348. var
  349.   Application: TMyTimeTest;
  350.  
  351. begin
  352.   Application := TMyTimeTest.Create(nil);
  353.   Application.Title := 'MyTimeTest';
  354.   Application.Run;
  355.   Application.Free;
  356. end.

Code: Text  [Select][+][-]
  1. {
  2.         "host":{
  3.                 "wired": "192.168.209.2",
  4.                 "usb_wifi": "192.168.25.5",
  5.                 "ports":[2000, 23000, 40000]   
  6.                 },
  7.         "port": {
  8.                 "wired": 20002,
  9.                 "usb_wifi": 20003
  10.                 },
  11.         "devices": ["uspd", "rip", "asr"],
  12.         "writedebug": True,
  13.         "writecriticalsection": True
  14.        
  15. }
Code: Text  [Select][+][-]
  1. {
  2.         "host":{
  3.                 "wired": "0.0.0.0",
  4.                 "usb_wifi": "0.0.0.0",
  5.                 "ports": [],   
  6.                 "modem_managed_0": "0.0.0.0"
  7.                 },
  8.                
  9.         "port": {
  10.                 "wired": 20002,
  11.                 "usb_wifi": 20002,
  12.                 "modem_managed_0": 20002
  13.                 },
  14.         "devices": [],
  15.         "writedebug": false,
  16.         "writecriticalsection": false,
  17.         "writemagicdebugid": -1,
  18.         "writembusdebugid": -1
  19.        
  20. }
« Last Edit: August 10, 2021, 09:19:12 am by blackspaceghost »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8835
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Trying to parse some JSON files
« Reply #1 on: August 10, 2021, 10:55:40 am »
Has memory leak, but probably close enough to what you want, note that neither nested arrays nor objects in arrays are supported, I'll leave that as an exercise for you:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   fpjson,
  5.   jsonparser;
  6.  
  7. const
  8.   JSON1Str = '{'
  9.            + '        "host":{'
  10.            + '                "wired": "192.168.209.2",'
  11.            + '                "usb_wifi": "192.168.25.5",'
  12.            + '                "ports":[2000, 23000, 40000]   '
  13.            + '                },'
  14.            + '        "port": {'
  15.            + '                "wired": 20002,'
  16.            + '                "usb_wifi": 20003'
  17.            + '                },'
  18.            + '        "devices": ["uspd", "rip", "asr"],'
  19.            + '        "writedebug": True,'
  20.            + '        "writecriticalsection": True'
  21.            + '       '
  22.            + '}'
  23.            ;
  24.  
  25.   JSON2Str = '{'
  26.            + '        "host":{'
  27.            + '                "wired": "0.0.0.0",'
  28.            + '                "usb_wifi": "0.0.0.0",'
  29.            + '                "ports": [],   '
  30.            + '                "modem_managed_0": "0.0.0.0"'
  31.            + '                },'
  32.            + '               '
  33.            + '        "port": {'
  34.            + '                "wired": 20002,'
  35.            + '                "usb_wifi": 20002,'
  36.            + '                "modem_managed_0": 20002'
  37.            + '                },'
  38.            + '        "devices": [],'
  39.            + '        "writedebug": false,'
  40.            + '        "writecriticalsection": false,'
  41.            + '        "writemagicdebugid": -1,'
  42.            + '        "writembusdebugid": -1'
  43.            + '       '
  44.            + '}'
  45.            ;
  46.  
  47. type
  48.   TJSONOverwriter = class
  49.     procedure Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  50.   end;
  51.  
  52.   TJSONObjectPlusPathMarker = class(TJSONObject)
  53.     Path: String;
  54.   end;
  55.  
  56. procedure TJSONOverwriter.Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  57. var
  58.   LPrevPath: String;
  59.   LJSONRef: TJSONObjectPlusPathMarker absolute Data;
  60.   LJSONRefData: TJSONData;
  61.   LJSONTempEnum: TJSONEnum;
  62.   LItemJSONArr: TJSONArray absolute Item;
  63. begin
  64.   LPrevPath := LJSONRef.Path;
  65.   if LJSONRef.Path = '' then
  66.     LJSONRef.Path := AName
  67.   else
  68.     LJSONRef.Path := Concat(LJSONRef.Path, '.', AName);
  69.  
  70.   case Item.JSONType of
  71.     jtUnknown: Halt(1);
  72.     jtNumber,jtString,jtBoolean,jtNull: begin
  73.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  74.       if Assigned(LJSONRefData) then begin
  75.         Item.Value := LJSONRefData.Value;
  76.       end;
  77.     end;
  78.     jtArray: begin
  79.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  80.       if Assigned(LJSONRefData) then begin
  81.         LItemJSONArr.Clear;
  82.         for LJSONTempEnum in LJSONRefData do
  83.           LItemJSONArr.Add(LJSONTempEnum.Value.Clone);
  84.       end;
  85.     end;
  86.     jtObject: begin
  87.       (Item as TJSONObject).Iterate(@Overwrite, Data);
  88.     end;
  89.   end;
  90.  
  91.   LJSONRef.Path := LPrevPath;
  92. end;
  93.  
  94. var
  95.   JSON1Obj: TJSONObjectPlusPathMarker;
  96.   JSON2Obj: TJSONObject;
  97.   FormatOptions: TFormatOptions;
  98. begin
  99.   JSON1Obj := TJSONObjectPlusPathMarker(GetJSON(JSON1Str));
  100.   JSON2Obj := TJSONObject(GetJSON(JSON2Str));
  101.   with TJSONOverwriter.Create do
  102.     try
  103.       JSON2Obj.Iterate(@Overwrite,JSON1Obj);
  104.       FormatOptions := DefaultFormat;
  105.       Include(FormatOptions, foSingleLineArray);
  106.       WriteLn(JSON2Obj.FormatJSON(FormatOptions, 2));
  107.     finally
  108.       Free;
  109.       JSON1Obj.Free;
  110.       JSON2Obj.Free;
  111.     end
  112. end.
  113.  

blackspaceghost

  • New Member
  • *
  • Posts: 14
Re: Trying to parse some JSON files
« Reply #2 on: August 10, 2021, 12:05:37 pm »
Has memory leak, but probably close enough to what you want, note that neither nested arrays nor objects in arrays are supported, I'll leave that as an exercise for you:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   fpjson,
  5.   jsonparser;
  6.  
  7. const
  8.   JSON1Str = '{'
  9.            + '        "host":{'
  10.            + '                "wired": "192.168.209.2",'
  11.            + '                "usb_wifi": "192.168.25.5",'
  12.            + '                "ports":[2000, 23000, 40000]   '
  13.            + '                },'
  14.            + '        "port": {'
  15.            + '                "wired": 20002,'
  16.            + '                "usb_wifi": 20003'
  17.            + '                },'
  18.            + '        "devices": ["uspd", "rip", "asr"],'
  19.            + '        "writedebug": True,'
  20.            + '        "writecriticalsection": True'
  21.            + '       '
  22.            + '}'
  23.            ;
  24.  
  25.   JSON2Str = '{'
  26.            + '        "host":{'
  27.            + '                "wired": "0.0.0.0",'
  28.            + '                "usb_wifi": "0.0.0.0",'
  29.            + '                "ports": [],   '
  30.            + '                "modem_managed_0": "0.0.0.0"'
  31.            + '                },'
  32.            + '               '
  33.            + '        "port": {'
  34.            + '                "wired": 20002,'
  35.            + '                "usb_wifi": 20002,'
  36.            + '                "modem_managed_0": 20002'
  37.            + '                },'
  38.            + '        "devices": [],'
  39.            + '        "writedebug": false,'
  40.            + '        "writecriticalsection": false,'
  41.            + '        "writemagicdebugid": -1,'
  42.            + '        "writembusdebugid": -1'
  43.            + '       '
  44.            + '}'
  45.            ;
  46.  
  47. type
  48.   TJSONOverwriter = class
  49.     procedure Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  50.   end;
  51.  
  52.   TJSONObjectPlusPathMarker = class(TJSONObject)
  53.     Path: String;
  54.   end;
  55.  
  56. procedure TJSONOverwriter.Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  57. var
  58.   LPrevPath: String;
  59.   LJSONRef: TJSONObjectPlusPathMarker absolute Data;
  60.   LJSONRefData: TJSONData;
  61.   LJSONTempEnum: TJSONEnum;
  62.   LItemJSONArr: TJSONArray absolute Item;
  63. begin
  64.   LPrevPath := LJSONRef.Path;
  65.   if LJSONRef.Path = '' then
  66.     LJSONRef.Path := AName
  67.   else
  68.     LJSONRef.Path := Concat(LJSONRef.Path, '.', AName);
  69.  
  70.   case Item.JSONType of
  71.     jtUnknown: Halt(1);
  72.     jtNumber,jtString,jtBoolean,jtNull: begin
  73.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  74.       if Assigned(LJSONRefData) then begin
  75.         Item.Value := LJSONRefData.Value;
  76.       end;
  77.     end;
  78.     jtArray: begin
  79.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  80.       if Assigned(LJSONRefData) then begin
  81.         LItemJSONArr.Clear;
  82.         for LJSONTempEnum in LJSONRefData do
  83.           LItemJSONArr.Add(LJSONTempEnum.Value.Clone);
  84.       end;
  85.     end;
  86.     jtObject: begin
  87.       (Item as TJSONObject).Iterate(@Overwrite, Data);
  88.     end;
  89.   end;
  90.  
  91.   LJSONRef.Path := LPrevPath;
  92. end;
  93.  
  94. var
  95.   JSON1Obj: TJSONObjectPlusPathMarker;
  96.   JSON2Obj: TJSONObject;
  97.   FormatOptions: TFormatOptions;
  98. begin
  99.   JSON1Obj := TJSONObjectPlusPathMarker(GetJSON(JSON1Str));
  100.   JSON2Obj := TJSONObject(GetJSON(JSON2Str));
  101.   with TJSONOverwriter.Create do
  102.     try
  103.       JSON2Obj.Iterate(@Overwrite,JSON1Obj);
  104.       FormatOptions := DefaultFormat;
  105.       Include(FormatOptions, foSingleLineArray);
  106.       WriteLn(JSON2Obj.FormatJSON(FormatOptions, 2));
  107.     finally
  108.       Free;
  109.       JSON1Obj.Free;
  110.       JSON2Obj.Free;
  111.     end
  112. end.
  113.  

thank you very much!
P.S. But , actually, your example is really too complex for my lvl of understanding and i cant add downloading JSON from file(( some errors are appears
« Last Edit: August 10, 2021, 12:41:47 pm by blackspaceghost »

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Trying to parse some JSON files
« Reply #3 on: August 10, 2021, 08:11:09 pm »
Using mormot library this boils down to:
Code: Pascal  [Select][+][-]
  1. procedure OverwriteSameKeys(d1,d2:PDocVariantData);
  2. var
  3.   key:string;
  4.   i1,i2:integer;
  5. begin
  6.   for i1:=0 to d1.Count-1 do
  7.   begin
  8.     key:=d1.Names[i1];
  9.     i2:=d2.GetValueIndex(key);
  10.     if (i2<>-1) then
  11.     begin
  12.       case d2._[i2].Kind of
  13.         dvObject: OverwriteSameKeys(d1._[i1],d2._[i2]);
  14.       otherwise
  15.         d2.Value[i2]:=d1.Value[i1];
  16.       end;
  17.     end;
  18.   end;
  19. end;

Sample project:
Code: Pascal  [Select][+][-]
  1. program transfer;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch autoderef}
  5.  
  6. uses
  7.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  8.   cthreads,
  9.   {$ENDIF}{$ENDIF}
  10.   Classes,SysUtils,
  11.   mormot.core.text,//<--- for jsonHumanReadable
  12.   mormot.core.os,//<--- for StringFromFile
  13.   mormot.core.json,//<---- for IsValidJson
  14.   mormot.core.variants
  15.   { you can add units after this };
  16.  
  17.  
  18. procedure OverwriteSameKeys(d1,d2:PDocVariantData);
  19. var
  20.   key:string;
  21.   i1,i2:integer;
  22. begin
  23.   for i1:=0 to d1.Count-1 do
  24.   begin
  25.     key:=d1.Names[i1];
  26.     i2:=d2.GetValueIndex(key);
  27.     if (i2<>-1) then
  28.     begin
  29.       case d2._[i2].Kind of
  30.         dvObject: OverwriteSameKeys(d1._[i1],d2._[i2]);
  31.       otherwise
  32.         d2.Value[i2]:=d1.Value[i1];
  33.       end;
  34.     end;
  35.   end;
  36. end;
  37.  
  38. function FileToJsonVariant(const AFileName:String; var v:variant):boolean;
  39. var
  40.   s:string;
  41. begin
  42.   Result:=False;
  43.   s:=StringFromFile(AFileName);
  44.   Result:=IsValidJson(s);
  45.   if Result then
  46.       v:=_Json(s)
  47.   else
  48.     WriteLn('File ',AFileName,' does not contain a valid json string.');
  49. end;
  50.  
  51. var
  52.   path:string;
  53.   d1,d2:TDocVariantData;
  54.   v1:variant absolute d1;
  55.   v2:variant absolute d2;
  56. begin
  57.   try
  58.     path:=ExtractFilePath(ParamStr(0));
  59.     if not FileToJsonVariant(path+'u1.json',v1) then exit;
  60.     if not FileToJsonVariant(path+'u2.json',v2) then exit;
  61.  
  62.     OverwriteSameKeys(@d1,@d2);
  63.  
  64.     WriteLn(d2.ToJson('','',jsonHumanReadable));
  65.   except
  66.     on e:Exception do begin
  67.           WriteLn('Exception:');
  68.           WriteLn(e.Message);
  69.     end;
  70.   end;
  71.   ReadLn;
  72. end.

When tested on your sample files, it rejected the first file u1 as invalid. It did not like your capitalized boolean: True.
Changing it to true made it happy.

avk

  • Hero Member
  • *****
  • Posts: 825
Re: Trying to parse some JSON files
« Reply #4 on: August 11, 2021, 08:44:06 am »
Yep, the code using FpJson looks surprisingly complicated. Using LGenerics, it might look something like this:
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SysUtils, lgUtils, lgJson;
  7.  
  8. var
  9.   Stack: array[0..TJsonNode.DEF_DEPTH] of string;
  10.   StackTop: Integer = -1;
  11.  
  12. procedure TryCopyValues(aSrc, aDst: TJsonNode);
  13. var
  14.   Node: TJsonNode;
  15.   p: TJsonNode.TPair;
  16. begin
  17.   if aDst.IsObject then
  18.     begin
  19.       if StackTop = TJsonNode.DEF_DEPTH then exit;
  20.       Inc(StackTop);
  21.       for p in aDst.Enrties do begin
  22.         Stack[StackTop] := p.Key;
  23.         TryCopyValues(aSrc, p.Value);
  24.       end;
  25.       Dec(StackTop);
  26.     end
  27.   else
  28.     if aSrc.FindPath(Stack[0..StackTop], Node) then
  29.       aDst.CopyFrom(Node);
  30. end;
  31.  
  32. var
  33.   jSrc, jDst: specialize TGUniqRef<TJsonNode>;
  34.   Node: TJsonNode;
  35. {$WARN 5090 OFF}
  36. begin
  37.   if ParamCount < 3 then
  38.     Halt(1);
  39.   if not(FileExists(ParamStr(1)) and TJsonNode.TryParseFile(ParamStr(1), Node)) then
  40.     Halt(1);
  41.   jSrc.Instance := Node;
  42.   if not(FileExists(ParamStr(2)) and TJsonNode.TryParseFile(ParamStr(2), Node)) then
  43.     Halt(1);
  44.   jDst.Instance := Node;
  45.   TryCopyValues(jSrc.Instance, jDst.Instance);
  46.   jDst.Instance.SaveToFile(ParamStr(3));
  47. end.
  48.  

blackspaceghost

  • New Member
  • *
  • Posts: 14
Re: Trying to parse some JSON files
« Reply #5 on: August 11, 2021, 09:35:06 am »
>> Using mormot library this boils down to:

I have cloned this library by git but cant to install it and cant find any some additional information for it. How to install this library for Typhoon64? i know, this question is so dumb, im sorry  :-[
« Last Edit: August 11, 2021, 09:48:09 am by blackspaceghost »

blackspaceghost

  • New Member
  • *
  • Posts: 14
Re: Trying to parse some JSON files
« Reply #6 on: August 11, 2021, 10:12:19 am »
Has memory leak, but probably close enough to what you want, note that neither nested arrays nor objects in arrays are supported, I'll leave that as an exercise for you:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   fpjson,
  5.   jsonparser;
  6.  
  7. const
  8.   JSON1Str = '{'
  9.            + '        "host":{'
  10.            + '                "wired": "192.168.209.2",'
  11.            + '                "usb_wifi": "192.168.25.5",'
  12.            + '                "ports":[2000, 23000, 40000]   '
  13.            + '                },'
  14.            + '        "port": {'
  15.            + '                "wired": 20002,'
  16.            + '                "usb_wifi": 20003'
  17.            + '                },'
  18.            + '        "devices": ["uspd", "rip", "asr"],'
  19.            + '        "writedebug": True,'
  20.            + '        "writecriticalsection": True'
  21.            + '       '
  22.            + '}'
  23.            ;
  24.  
  25.   JSON2Str = '{'
  26.            + '        "host":{'
  27.            + '                "wired": "0.0.0.0",'
  28.            + '                "usb_wifi": "0.0.0.0",'
  29.            + '                "ports": [],   '
  30.            + '                "modem_managed_0": "0.0.0.0"'
  31.            + '                },'
  32.            + '               '
  33.            + '        "port": {'
  34.            + '                "wired": 20002,'
  35.            + '                "usb_wifi": 20002,'
  36.            + '                "modem_managed_0": 20002'
  37.            + '                },'
  38.            + '        "devices": [],'
  39.            + '        "writedebug": false,'
  40.            + '        "writecriticalsection": false,'
  41.            + '        "writemagicdebugid": -1,'
  42.            + '        "writembusdebugid": -1'
  43.            + '       '
  44.            + '}'
  45.            ;
  46.  
  47. type
  48.   TJSONOverwriter = class
  49.     procedure Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  50.   end;
  51.  
  52.   TJSONObjectPlusPathMarker = class(TJSONObject)
  53.     Path: String;
  54.   end;
  55.  
  56. procedure TJSONOverwriter.Overwrite(const AName: TJSONStringType; Item: TJSONData; Data: TObject; var Continue: Boolean);
  57. var
  58.   LPrevPath: String;
  59.   LJSONRef: TJSONObjectPlusPathMarker absolute Data;
  60.   LJSONRefData: TJSONData;
  61.   LJSONTempEnum: TJSONEnum;
  62.   LItemJSONArr: TJSONArray absolute Item;
  63. begin
  64.   LPrevPath := LJSONRef.Path;
  65.   if LJSONRef.Path = '' then
  66.     LJSONRef.Path := AName
  67.   else
  68.     LJSONRef.Path := Concat(LJSONRef.Path, '.', AName);
  69.  
  70.   case Item.JSONType of
  71.     jtUnknown: Halt(1);
  72.     jtNumber,jtString,jtBoolean,jtNull: begin
  73.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  74.       if Assigned(LJSONRefData) then begin
  75.         Item.Value := LJSONRefData.Value;
  76.       end;
  77.     end;
  78.     jtArray: begin
  79.       LJSONRefData := LJSONRef.FindPath(LJSONRef.Path);
  80.       if Assigned(LJSONRefData) then begin
  81.         LItemJSONArr.Clear;
  82.         for LJSONTempEnum in LJSONRefData do
  83.           LItemJSONArr.Add(LJSONTempEnum.Value.Clone);
  84.       end;
  85.     end;
  86.     jtObject: begin
  87.       (Item as TJSONObject).Iterate(@Overwrite, Data);
  88.     end;
  89.   end;
  90.  
  91.   LJSONRef.Path := LPrevPath;
  92. end;
  93.  
  94. var
  95.   JSON1Obj: TJSONObjectPlusPathMarker;
  96.   JSON2Obj: TJSONObject;
  97.   FormatOptions: TFormatOptions;
  98. begin
  99.   JSON1Obj := TJSONObjectPlusPathMarker(GetJSON(JSON1Str));
  100.   JSON2Obj := TJSONObject(GetJSON(JSON2Str));
  101.   with TJSONOverwriter.Create do
  102.     try
  103.       JSON2Obj.Iterate(@Overwrite,JSON1Obj);
  104.       FormatOptions := DefaultFormat;
  105.       Include(FormatOptions, foSingleLineArray);
  106.       WriteLn(JSON2Obj.FormatJSON(FormatOptions, 2));
  107.     finally
  108.       Free;
  109.       JSON1Obj.Free;
  110.       JSON2Obj.Free;
  111.     end
  112. end.
  113.  

or how to extract by analogy JSON1Str from file? If i just make GetJSON(str1) from file, your Iterate function doesnt work properly(

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1290
Re: Trying to parse some JSON files
« Reply #7 on: August 11, 2021, 12:44:39 pm »
hello,
Leledumbo with your code i have a SIGSEGV with this line :
Code: Pascal  [Select][+][-]
  1.  LPrevPath := LJSONRef.Path;
see attachment
Windows 10 Lazarus 32 bits 2.0.12 fpc 3.2

Friendly, J.P
« Last Edit: August 11, 2021, 12:48:00 pm by Jurassic Pork »
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Trying to parse some JSON files
« Reply #8 on: August 11, 2021, 02:57:50 pm »
>> Using mormot library this boils down to:

I have cloned this library by git but cant to install it and cant find any some additional information for it. How to install this library for Typhoon64? i know, this question is so dumb, im sorry  :-[

I don't think it's a dumb question, because I don't know how to install any library on Typhoon64.  :-[

In Lazarus go to
Package - Open Package File. .... and choose from mORMot2 folder:
packages\lazarus\mormot2.lpk

Compile it, then Use - Add to Project

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Trying to parse some JSON files
« Reply #9 on: August 11, 2021, 07:35:06 pm »
Here is my attempt to use JSONTools after sysrpl added this to his library:
Code: Pascal  [Select][+][-]
  1. program usingJSONTools;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes,SysUtils,
  10.   JsonTools
  11.   { you can add units after this };
  12.  
  13. procedure OverwriteSameKeys(n1,n2:TJSONNode);
  14. var
  15.   i1:integer;
  16.   c1,c2:TJsonNode;
  17.   {a1,}a2:TJsonNode;
  18.   n:TJsonNode;
  19. begin
  20.   for i1:=0 to n1.Count-1 do
  21.   begin
  22.     c1:=n1.Child(i1);
  23.     c2:=n2.Child(c1.Name);
  24.     if (c2=nil) then continue;
  25.  
  26.     case c2.Kind of
  27.       nkObject: OverwriteSameKeys(c1,c2);
  28.       nkArray: begin
  29.         c2.Clear;
  30.         a2:=c2.Add(c1.Name).AsArray;
  31.         for n in c1 do
  32.           a2.Add.Value:=n.Value;
  33.       end;
  34.     otherwise
  35.       c2.Value:=c1.Value;
  36.     end;
  37.   end;
  38. end;
  39.  
  40. function FileToJsonNode(const AFileName:string; out n:TJSONNode):boolean;
  41. begin
  42.   Result:=False;
  43.   if FileExists(AFileName) then begin
  44.     with TStringStream.Create do try
  45.       LoadFromFile(AFileName);
  46.       if not JsonValidate(DataString) then exit;
  47.       n:=TJSONNode.Create;
  48.       n.Value:=DataString;
  49.     finally
  50.       Free;
  51.     end;
  52.     Result:=True;
  53.   end;
  54. end;
  55.  
  56. var
  57.   path:string;
  58.   n1,n2:TJSONNode;
  59. begin
  60.   try
  61.     path:=ExtractFilePath(ParamStr(0));
  62.     if not FileToJsonNode(path+'u1.json',n1) then exit;
  63.     if not FileToJsonNode(path+'u2.json',n2) then exit;
  64.  
  65.     OverwriteSameKeys(n1,n2);
  66.  
  67.     WriteLn(n2.ToString);
  68.   except
  69.     on e:Exception do begin
  70.           WriteLn('Exception:');
  71.           WriteLn(e.Message);
  72.     end;
  73.   end;
  74.   n1.Free;
  75.   n2.Free;
  76.   ReadLn;
  77. end.

sysrpl

  • Sr. Member
  • ****
  • Posts: 315
    • Get Lazarus
Re: Trying to parse some JSON files
« Reply #10 on: August 11, 2021, 08:07:20 pm »
Using my JsonTools library, here is a small routine to merge two json nodes.

Code: Pascal  [Select][+][-]
  1. procedure MergeNodes(Source, Dest: TJsonNode);
  2. var
  3.   Node: TJsonNode;
  4. begin
  5.   case Source.Kind of
  6.     nkArray:
  7.       begin
  8.         Dest.AsArray.Clear;
  9.         for Node in Source do
  10.           MergeNodes(Node, Dest.Add);
  11.       end;
  12.     nkObject:
  13.       begin
  14.         Dest.AsObject;
  15.         for Node in Source do
  16.           if Dest.Child(Node.Name) = nil then
  17.             MergeNodes(Node, Dest.Add(Node.Name))
  18.           else
  19.             MergeNodes(Node, Dest.Child(Node.Name));
  20.       end;
  21.   else
  22.     Dest.Value := Source.Value;
  23.   end;
  24. end;
  25.  
« Last Edit: August 11, 2021, 08:16:40 pm by sysrpl »

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1290
Re: Trying to parse some JSON files
« Reply #11 on: August 12, 2021, 12:52:56 am »
hello
hello,
Leledumbo with your code i have a SIGSEGV with this line :
Code: Pascal  [Select][+][-]
  1.  LPrevPath := LJSONRef.Path;
more infos with the sigsegv :
Code: Pascal  [Select][+][-]
  1. TJSONObjectPlusPathMarker = class(TJSONObject)
  2.     Path: String;
  3.   end;

Code: Pascal  [Select][+][-]
  1. var
  2.   JSON1Obj: TJSONObjectPlusPathMarker;
  3. begin
  4.   JSON1Obj := TJSONObjectPlusPathMarker(GetJSON(JSON1Str));
  5.   JSON1Obj.Path := 'test';
  6.    

SISEGV error on the line  JSON1Obj.Path := 'test';  ---> Why ?

[EDIT] Seems to be OK with this :
Code: Pascal  [Select][+][-]
  1. JSON1Obj :=  TJSONObjectPlusPathMarker.Create();
  2.   JSON1Obj.Add('TJSONObject',GetJSON(JSON1Str));  

but the program doesn't work ---> no merge


Friendly, J.P
« Last Edit: August 12, 2021, 01:57:06 am by Jurassic Pork »
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

Leledumbo

  • Hero Member
  • *****
  • Posts: 8835
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Trying to parse some JSON files
« Reply #12 on: August 12, 2021, 07:18:19 am »
or how to extract by analogy JSON1Str from file? If i just make GetJSON(str1) from file, your Iterate function doesnt work properly(
Just read the file in a single run:
Code: Pascal  [Select][+][-]
  1. with TStringList.Create do
  2.   try
  3.     LoadFromFile('/path/to/json/file');
  4.     TheJSONObject := TJSONObject(GetJSON(Text));
  5.   finally
  6.     Free;
  7.   end;
  8.  
I wish we have a one liner but AFAIK that's the shortest the RTL/FCL can give.
but the program doesn't work ---> no merge
I know it's classical, but it works on my machine and I believe I don't do anything platform specific:
Code: Bash  [Select][+][-]
  1. $ ./jsonoverwriteexample
  2. {
  3.   "host" : {
  4.     "wired" : "192.168.209.2",
  5.     "usb_wifi" : "192.168.25.5",
  6.     "ports" : [2000, 23000, 40000],
  7.     "modem_managed_0" : "0.0.0.0"
  8.   },
  9.   "port" : {
  10.     "wired" : 20002,
  11.     "usb_wifi" : 20003,
  12.     "modem_managed_0" : 20002
  13.   },
  14.   "devices" : ["uspd", "rip", "asr"],
  15.   "writedebug" : true,
  16.   "writecriticalsection" : true,
  17.   "writemagicdebugid" : -1,
  18.   "writembusdebugid" : -1
  19. }
  20.  

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1290
Re: Trying to parse some JSON files
« Reply #13 on: August 12, 2021, 10:27:07 am »
hello,
leledumbo, i have tried your program under ubuntu 20.04  Lazarus 2.1  fpc 3.3.1---> it's OK  . Under Windows 10  Lazarus 2.0.12  fpc 3.2 it's not OK   SIGSEGV error

may be there is a bug under windows 10 or fpc 3.2
What is your Lazarus fpc versions ?

Friendly , J.P
« Last Edit: August 12, 2021, 10:31:14 am by Jurassic Pork »
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

blackspaceghost

  • New Member
  • *
  • Posts: 14
Re: Trying to parse some JSON files
« Reply #14 on: August 12, 2021, 02:17:25 pm »
or how to extract by analogy JSON1Str from file? If i just make GetJSON(str1) from file, your Iterate function doesnt work properly(
Just read the file in a single run:
Code: Pascal  [Select][+][-]
  1. with TStringList.Create do
  2.   try
  3.     LoadFromFile('/path/to/json/file');
  4.     TheJSONObject := TJSONObject(GetJSON(Text));
  5.   finally
  6.     Free;
  7.   end;
  8.  
I wish we have a one liner but AFAIK that's the shortest the RTL/FCL can give.
but the program doesn't work ---> no merge
I know it's classical, but it works on my machine and I believe I don't do anything platform specific:
Code: Bash  [Select][+][-]
  1. $ ./jsonoverwriteexample
  2. {
  3.   "host" : {
  4.     "wired" : "192.168.209.2",
  5.     "usb_wifi" : "192.168.25.5",
  6.     "ports" : [2000, 23000, 40000],
  7.     "modem_managed_0" : "0.0.0.0"
  8.   },
  9.   "port" : {
  10.     "wired" : 20002,
  11.     "usb_wifi" : 20003,
  12.     "modem_managed_0" : 20002
  13.   },
  14.   "devices" : ["uspd", "rip", "asr"],
  15.   "writedebug" : true,
  16.   "writecriticalsection" : true,
  17.   "writemagicdebugid" : -1,
  18.   "writembusdebugid" : -1
  19. }
  20.  
thanks! it works!

 

TinyPortal © 2005-2018