Recent

Author Topic: [SOLVED] How to traverse a JSON object for a specific tag using fcl-json?  (Read 4715 times)

FangQ

  • Full Member
  • ***
  • Posts: 134
I recently found fcl-json and used it in my program. I found it very convenient. One thing I would like to do is to traverse a JSON data structure and find the maximum value of a specific tag ("Tag": an_integer_value). This tag is included in multiple objects at multiple levels.

I wrote the below code for 1) updating the root JSON object with the modified element values at the leaves, and 2) search for the "Tag" field in the first level child objects and find the maximum value, however, I am getting some Access violation errors. The TTreeView with the attached JSON data structure were created using the ShowJSONData() function in this link

http://wiki.freepascal.org/fcl-json#From_JsonViewer

with some tweaking. any comment on the traversing or the Access Errors would be appreciated.

Code: Pascal  [Select][+][-]
  1. function TfmMCX.RebuildShapeJSON(root: TTreeNode): integer;
  2. var
  3.      i, cc: integer;
  4.      jdata: TJSONData;
  5. begin
  6.      Result:=0;
  7.      jdata:=GetJSON('{"Shapes": []}');
  8.      for i:=0 to root.Count-1 do begin
  9.          if(Assigned(root.Items[i].Data)) then begin
  10.             TJSONArray(jdata.Items[0]).Add(TJSONObject(root.Items[i].Data));
  11.             if(TJSONObject(root.Items[i].Data).Count=0) then continue;
  12.             cc:= TJSONObject(root.Items[i].Data).Items[0].Count;
  13.             if(TJSONObject(root.Items[i].Data).Items[0].FindPath('Tag') <> nil) then
  14.                 Result:=Max(Result,TJSONObject(TJSONObject(root.Items[i].Data).Items[0]).Integers['Tag']);
  15.          end;
  16.      end;
  17.      root.Data:=jdata;
  18. end;
« Last Edit: July 06, 2017, 04:28:40 am by FangQ »

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: How to traverse a JSON object for a specific tag using fcl-json?
« Reply #1 on: June 26, 2017, 08:19:27 pm »
by the way, the access violation error sometimes was thrown from the line

Code: Pascal  [Select][+][-]
  1.             if(TJSONObject(root.Items[i].Data).Count=0) then continue;
  2.  

isn't count defined for all valid JSONobject?

Leledumbo

  • Hero Member
  • *****
  • Posts: 8747
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: How to traverse a JSON object for a specific tag using fcl-json?
« Reply #2 on: June 26, 2017, 08:48:12 pm »
by the way, the access violation error sometimes was thrown from the line

Code: Pascal  [Select][+][-]
  1.             if(TJSONObject(root.Items[i].Data).Count=0) then continue;
  2.  

isn't count defined for all valid JSONobject?
Yes, but the exception can still be raised from the cast (what if root.Items.Data is not a TJSONObject?). It happens even before Count is accessed.

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: How to traverse a JSON object for a specific tag using fcl-json?
« Reply #3 on: June 26, 2017, 10:55:18 pm »
@Leledumbo, thanks for your comment.

Though, the same pointer, root.Items.Data, was tested (Assigned(...)) and used (TJSONArray(jdata.Items[0]).Add(..)) in the above lines, and did not fire an error.

The TreeView was built so that the Data field of each TreeNode is either nil, or a pointer pointing to a TJSONData. So it should be either one of the two cases. I also tried to cast the data to TJSONData(...).Count instead of TJSONObject, still I received the access violation error.

I guess the root of my question is how to deal with the memory management of TJSONData when it's elements are mapped to a TTreeView. From the built-in example, jsonviewer, I see only the root object (FRoot) is explicitly managed via FreeAndNil(). All editing operations, such as deleting or adding TreeNode (thus the data linked in the JSON object) does not involve freeing memory. Is that the proper way of managing the JSON object?

I used a lot of dynamic JSON object creation such as

Code: Pascal  [Select][+][-]
  1. ShowJSONData(node,GetJSON('{"obj1":1}'));

or

or dynamic modification

Code: Pascal  [Select][+][-]
  1. procedure TfmMCX.TreeViewEdited(Sender: TObject; Node: TTreeNode; var S: string);
  2.     ...
  3.     val:=StrToFloat(S);
  4.     TJSONArray(TJSONArray(Node.Parent.Data).Items[Node.Parent.Index]).Integers[Node.Index]:=Round(val);
  5.     ...
  6. end;

wondering if I need to free anything before I modify the subitems of each JSON element.
« Last Edit: June 26, 2017, 11:03:19 pm by FangQ »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8747
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: How to traverse a JSON object for a specific tag using fcl-json?
« Reply #4 on: June 26, 2017, 11:16:35 pm »
All editing operations, such as deleting or adding TreeNode (thus the data linked in the JSON object) does not involve freeing memory. Is that the proper way of managing the JSON object?
Depends. As long as the TJSONData descendant is still accessible, e.g. part of a JSON tree whose root are still accessible, there's no need to manually free every node. Freeing the root will free all of its children as well. The data structure holding the children (a TFP[Hash]ObjectList instance) is created with parameter true, which means its contents will be freed as soon as the instance is freed. Watch out for double free or Clone result, though.

Another solution, if you don't mind using FPC trunk and gc overhead, there's a libgc unit that implements memory manager based on Boehm GC.

FangQ

  • Full Member
  • ***
  • Posts: 134
Re: How to traverse a JSON object for a specific tag using fcl-json?
« Reply #5 on: July 06, 2017, 04:28:29 am »
Depends. As long as the TJSONData descendant is still accessible, e.g. part of a JSON tree whose root are still accessible, there's no need to manually free every node. Freeing the root will free all of its children as well. The data structure holding the children (a TFP[Hash]ObjectList instance) is created with parameter true, which means its contents will be freed as soon as the instance is freed. Watch out for double free or Clone result, though.

Another solution, if you don't mind using FPC trunk and gc overhead, there's a libgc unit that implements memory manager based on Boehm GC.

you were absolutely right! I went through my code and made a bunch of changes, now the JSON related memory leak has gone! here are a few things I have learned

1. the ShowJSONData does not either create or remove nodes from a JSON structure, it merely maps the JSON leave pointers to a TTreeView. therefore, whenever one wants to modify the JSON data, he/she has to make modifications directly to the JSON data itself, then call ShowJSONData to refresh the pointer mappings. As you said, as long as the JSON leaves are accessible via some root object, a FreeAndNil() at the FormDestroy event will free everything.

2. Always use TJSONData/TJSONArray/TJSONObject's Add/Remove/assignment methods to add/remove/modify the JSON data structure. As long as you modify the main data structure using its own methods, the nodes' memories are managed.

3. A safe (yet a little bit inefficient) way to create/modify JSON sub-structure is to use strings - inside a function, one can create a JSON object, add all needed nodes, and then save as a string by JSONData.AsJSON, then you can FreeAndNil that object right away, and return the JSON string. In the calling function, use GetJSON() to recreate that object and attach it to the base JSON data. This way, each function becomes self-contained. It is easier to debug the memory leak.

 

TinyPortal © 2005-2018