msd 5 Posted December 24, 2021 Hello, I found that Delphi has a nice classes for JSON objects TJsonTextReader & TJsonTextWriter and I have one quesion for the TJsonTextReader. How to extract only search node from bottom JSON sample (or columns node or colors node, I need only one node for processing out of whole JSON) { "columns":[ { "column":"AccountID", "caption":"Account", "width":"100" }, { "column":"Name", "caption":"Account Name", "width":"250" }, { "column":"IsActive", "caption":"A", "width":"30" } ], "search":[ { "column":"Account", "rules":"Asc" }, { "column":"Name", "rules":"Asc;Part" } ], "colors":[ { "column":"Account", "rules":"Red;White" }, { "column":"Name", "rules":"Blue;Black" } ] } Thanks for the help and assistance in the advance 🙂 Share this post Link to post
Remy Lebeau 1393 Posted December 24, 2021 (edited) Using TJSONTextReader, you can parse the document like this: uses ..., System.Generics.Collections, System.JSON.Types, System.JSON.Readers; type TColumnNode = record Column: string; Caption: string; Width: string; end; TSearchNode = record Column: string; Rules: string; end; TColorNode = record Column: string; Rules: string; end; TReadState = ( None, InTopObject, InColumnsProp, InColumnsArray, InColumnObject, InSearchProp, InSearchArray, InSearchObject, InColorsProp, InColorsArray, InColorObject ); var JSON: string; Columns: TList<TColumnNode>; Search: TList<TSearchNode>; Colors: TList<TColorNode>; ColumnNode: TColumnNode; SearchNode: TSearchNode; ColorNode: TColorNode; TextReader: TTextReader; JSONReader: TJSONTextReader; State: TReadState; PropertyName: string; begin Columns := nil; Search := nil; Colors := nil; JSON := '...'; try Columns := TList<TColumnNode>.Create; Search := TList<TSearchNode>.Create; Colors := TList<TColorNode>.Create; TextReader := TStringReader.Create(JSON); try JSONReader := TJSONTextReader.Create(TextReader); try State := TReadState.None; while JSONReader.Read do begin case JSONReader.TokenType of TJsonToken.StartObject: begin case State of TReadState.None: State := TReadState.InTopObject; TReadState.InColumnsArray: begin ColumnNode := Default(TColumnNode); State := TReadState.InColumnObject; end; TReadState.InSearchArray: begin SearchNode := Default(TSearchNode); State := TReadState.InSearchObject; end; TReadState.InColorsArray: begin ColorNode := Default(TColorNode); State := TReadState.InColorObject; end; end; end; TJsonToken.EndObject: begin case State of TReadState.InTopObject: State := TReadState.None; TReadState.InColumnObject: begin Columns.Add(ColumnNode); State := TReadState.InColumnsArray; end; TReadState.InSearchObject: begin Search.Add(SearchNode); State := TReadState.InSearchArray; end; TReadState.InColorObject: begin Colors.Add(ColorNode); State := TReadState.InColorsArray; end; end; end; TJsonToken.StartArray: begin case State of TReadState.InColumnsProp: State := TReadState.InColumnsArray; TReadState.InSearchProp: State := TReadState.InSearchArray; TReadState.InSearchProp: State := TReadState.InColorsArray; end; end; TJsonToken.EndArray: begin case State of TReadState.InColumnsArray, TReadState.InSearchArray, TReadState.InColorsArray: State := TReadState.InTopObject; end; end; TJsonToken.PropertyName: begin PropertyName := JSONReader.Value.AsString; case State of TReadState.InTopObject: begin if PropertyName = 'columns' then State := TReadState.InColumnsProp end else if PropertyName = 'search' then State := TReadState.InSearchProp end else if PropertyName = 'colors' then State := TReadState.InColorsProp else JSONReader.Skip; end; TReadState.InColumnObject: begin if PropertyName = 'column' then CurrColumn.Column := JSONReader.ReadAsString else if PropertyName = 'caption' then CurrColumn.Caption := JSONReader.ReadAsString else if PropertyName = 'width' then CurrColumn.Width := JSONReader.ReadAsString else JSONReader.Skip; end; TReadState.InSearchObject: begin if PropertyName = 'column' then CurrSearch.Column := JSONReader.ReadAsString else if PropertyName = 'rules' then CurrSearch.Rules := JSONReader.ReadAsString else JSONReader.Skip; end; TReadState.InColorObject: begin if PropertyName = 'column' then CurrColor.Column := JSONReader.ReadAsString else if PropertyName = 'rules' then CurrColor.Rules := JSONReader.ReadAsString else JSONReader.Skip; end; else JSONReader.Skip; end; end; end; end; finally JSONReader.Free; end; finally TextReader.Free; end; // use Columns, Search, and Colors as needed... finally Columns.Free; Search.Free; Colors.Free; end; end; Obviously, take out whatever pieces you don't need. But needless to say, using a Reader can keep memory usage to a minimum, but at the cost of complicating the parsing logic. If your document is not very large, you might consider using a little extra memory in order to utilize the DOM-based framework instead, eg: uses ..., System.Generics.Collections, System.JSON; type TColumnNode = record Column: string; Caption: string; Width: string; end; TSearchNode = record Column: string; Rules: string; end; TColorNode = record Column: string; Rules: string; end; var JSON: string; Columns: TList<TColumnNode>; Search: TList<TSearchNode>; Colors: TList<TColorNode>; ColumnNode: TColumnNode; SearchNode: TSearchNode; ColorNode: TColorNode; JSONValue: TJSONValue; JSONObject: TJSONObject; JSONArray: TJSONArray; ArrElement: TJSONValue; begin Columns := nil; Search := nil; Colors := nil; JSON := '...'; try Columns := TList<TColumnNode>.Create; Search := TList<TSearchNode>.Create; Colors := TList<TColorNode>.Create; JSONValue := TJSONObject.ParseJSONValue(JSON); if JSONValue <> nil then try JSONObject := JSONValue as TJSONObject; JSONArray := JSONObject.GetValue('columns') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin ColumnNode.Column := GetValue('column').Value; ColumnNode.Caption := GetValue('caption').Value; ColumnNode.Width := GetValue('width').Value; Columns.Add(ColumnNode); end; end; JSONArray := JSONObject.GetValue('search') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin SearchNode.Column := GetValue('column').Value; SearchNode.Rules := GetValue('rules').Value; Search.Add(SearchNode); end; end; JSONArray := JSONObject.GetValue('colors') as TJSONArray; for ArrElement in JSONArray do begin with ArrElement as JSONObject do begin ColorNode.Column := GetValue('column').Value; ColorNode.Rules := GetValue('rules').Value; Colors.Add(ColorNode); end; end; finally JSONValue.Free; end; // use Columns, Search, and Colors as needed... finally Columns.Free; Search.Free; Colors.Free; end; end; Edited December 24, 2021 by Remy Lebeau Share this post Link to post
msd 5 Posted December 24, 2021 Hello Remy Lebeau, Two very good ideas :-) I plan to use JsonReader because of memory usage and it is fresh tech in RAD Studio, but you give me very good tip. Thanks once again for the all advices... Share this post Link to post