Jump to content
msd

TJsonTextReader - extract object

Recommended Posts

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

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 by Remy Lebeau

Share this post


Link to post

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×