Jump to content
direktor05

Parse Json again, complicated

Recommended Posts

Hello,

 

How do I parse this Json:

 

{"result": [{
      "animals":
      [{
          "id":1,

         "name":

 

 

So.... I parse Json first:

jsonobject := TJsonObject.ParseJSONValue(RestResponse.Content) as TJsonObject;

 

Then I get to "animals":[{"id".... now here gets complicated. How do I parse further to get ID and name? Parse Json further or parse Json Array? Can someone help with some example code please?

Share this post


Link to post

Full Json:

 

{"result":  [{
      "animals":
      [{
          "id":1,
          "name":"pig",

        } ]
    } ]
}

Share this post


Link to post

Hello,

 

Your "complete" json is not valid. You should edit your post and fix it.

 

There are more than one way to solve this. Below is an example with a lot of error checks.

type
  TAnimal = record
    id: Integer;
    name: string;
  end;
  TAnimalList = TArray<TAnimal>;
//-----------------------------------
implementation
//-----------------------------------
uses
  System.JSON,
  System.Generics.Collections;

const
  JsonString = '{"result":[{"animals":[{"id":1,"name":"pig"}]}]}';

function ParseAnimalsJson(const Json: string; out AnimalList: TAnimalList): Boolean;
var
  LTestJsonValue: TJSONValue;
  LJson: TJSONValue;
  LJsonArrayResult: TJSONArray;
  LJsonArrayAnimals: TJSONArray;
  I: Integer;
begin
  LTestJsonValue := TJSONObject.ParseJSONValue(Json, False, False);
  if LTestJsonValue = nil then
  begin
    Exit(False);
  end;

  try
    LJson := LTestJsonValue.FindValue('result');
    if LJson = nil then
      Exit(False);

    if not (LJson is TJSONArray) then
      Exit(False);

    LJsonArrayResult := LJson as TJSONArray;
    if LJsonArrayResult.Count = 0 then
      Exit(False);

    LJson := LJsonArrayResult[0].FindValue('animals');
    if LJson = nil then
      Exit(False);

    if not (LJson is TJSONArray) then
      Exit(False);

    LJsonArrayAnimals := LJson as TJSONArray;
    SetLength(AnimalList, LJsonArrayAnimals.Count);
    for I := 0 to LJsonArrayAnimals.Count-1 do
    begin
      LJson := LJsonArrayAnimals[I].FindValue('id');
      if LJson = nil then Exit(False);
      AnimalList[I].id := LJson.AsType<Integer>;

      LJson := LJsonArrayAnimals[I].FindValue('name');
      if LJson = nil then Exit(False);
      AnimalList[I].name := LJson.AsType<string>;
    end;
  finally
    LTestJsonValue.Free();
  end;
  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  List: TAnimalList;
begin
  if ParseAnimalsJson(JsonString, List) then
  begin
    ShowMessage('Parse completed');
  end
  else
  begin
    ShowMessage('Parse failed!');
  end;
end;

 

Edited by ertank

Share this post


Link to post

Do you know about https://jsontodelphi.com/ website? If you are in a hurry then go there generate your classes dto's. Copy and past your json string and they will generate a dto boilerplate for you. The only problem is that this boilerplate uses generics and cannot be implemented in old versions of Delphi. Otherwise, check out Super Object in github project that is aimed for these cases.

Edited by p-samuel

Share this post


Link to post
15 hours ago, direktor05 said:

Then I get to "animals":[{"id".... now here gets complicated. How do I parse further to get ID and name? Parse Json further or parse Json Array? Can someone help with some example code please?

Let's say you have a TJSONObject variable which holds the inmost object only (id and name) named innerjson:

if innerjson.GetValue('id') <> null then fid := (innerjson.GetValue('id') As TJSONNumber).AsLargeInt;
if innerjson.GetValue('name') <> null then fname := (innerjson.GetValue('name') As TJSONString).Value;

if you are sure that these will always be present, you can discard the nullcheck.

Share this post


Link to post

If you create a class for this, then there's a method that can theoretically create an instance of it and fill it from a chunk of JSON data:
 

var data_obj := TJson.JsonToObject<TMyObj>( req.Response );

In this case, the JSON data would go into a class TMyObj, which is the type returned in data_obj.

 

I've used it for simple objects, but I'm not sure how it would handle lists and arrays. 

 

BTW, that JSON has an error in it -- a dangling comma

 

Share this post


Link to post

There are a few things to resolve before getting to "animal id".
This is just an example using Delphi 11, but older version should work too. (Might need to remove the inline variable).
This is how I would do it:
 

unit Unit94;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  System.JSON;

type
  TForm94 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    const
      _JSON = ' {"result":  [{ "animals": [{ "id":1, "name":"pig" } ] } ] }';
  public
    { Public declarations }
  end;

var
  Form94: TForm94;

implementation

{$R *.dfm}

procedure TForm94.Button1Click(Sender: TObject);
var
  lJsResult : TJsonObject;
  lJsResultArray  : TJsonArray;
  lJsAnimals      : TJsonObject;
  lJsAnimalArray  : TJsonArray;
  lJsAnimal       : TJsonObject;

  lId : Integer;
  lname : String;
begin
   lJsResult := TJSONObject.ParseJSONValue(_JSON) as TJsonObject;
   try
     if lJsResult.TryGetValue<TJsonArray>('result', lJsResultArray) then begin

       for var r := 0 to lJsResultArray.Count-1 do begin
          lJsAnimals := lJsResultArray.Items[r] as TJsonObject;
          lJsAnimalArray := lJsAnimals.GetValue<TJsonArray>('animals');
          for var i := 0 to lJsAnimalArray.Count-1 do begin
             lJsAnimal := lJsAnimalArray.Items[i] as TJSONObject;
             if not lJsAnimal.TryGetValue<Integer>('id', lId ) then raise Exception.Create('No "ID" Field');
             if not lJsAnimal.TryGetValue<String>('name', lname) then raise Exception.Create('No "name" Field');

             // DO something with lId, and name

          end;
       end;
     end
   finally
     lJsResult.Free;
   end;

end;

 

Share this post


Link to post

use this object, very simple:
https://github.com/hgourvest/superobject

 

Ex:

 

Function ...

var

  jsonPedido            : ISuperObject;

begin

 jsonPedido := SO(aPedido);

  if jsonPedido.Count > 0 then
  begin
    idPedido        := jsonPedido.S['codigo'];
    merchanId       := IntToStr(jsonPedido.I['idLoja']);
    totalPrice      := jsonPedido.F['valorCorrigido'];
    pedidoCreatAt   := jsonPedido.D['data'];
    idContato       := IntToStr(jsonPedido.O['cliente'].I['id']);
  end;

end;

Edited by eddie Alves

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

×