1
votes

I'm trying to catch the following JSON array :

[{"name":"Bryan","email":"[email protected]"},
 {"name":"Louis","email":"[email protected]"},
 {"name":"Maria","email":"[email protected]"},
 {"name":"Test","email":"[email protected]"},
 {"name":"Anthony","email":"[email protected]"}]

and put it in Memo or ListBox in Delphi : the code is the following :

procedure TForm1.Button1Click(Sender: TObject);
var jv: TJSONValue;
    jo: TJSONObject;
    jp: TJSONPair;
    ja: TJSONArray;
    i: integer;
    j: integer;
begin
    RESTRequest1.Execute;

    jv:=RESTResponse1.JSONValue;


    jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;

    try
      for i := 0 to jo.Size - 1 do
      begin
        jp := jo.Get(i);
        if jp.JsonValue is TJSONArray then
        begin
            ja := jp.JsonValue as TJSONArray;
            for j := 0 to ja.Size -1 do
              Memo1.Lines.Add(ja.Get(i).ClassName + ': ' + ja.Get(j).ToString);
        end
        else
          Memo1.Lines.Add(jp.ClassName + ': '+ jp.ToString);

      end;
    finally
      jo.Free;
    end;
end;

When I click in Button I got the following error message :

Invalid class typecast

during debugging the following line has a problem : jo:= TJSONObject.ParseJSONValue(jv.ToString) as TJSONObject;

I don't know how to resolve this problem or this mistake , Could you please help me ?

Thanks.

2
Clearly the value returned from TJSONObject.ParseJSONValue(jv.ToString) is not derived from TJSONObject. Perhaps it is nil. You can debug this quite easily. If you wanted us to help you'd need to produce an MCVE. It would be very easy to do so. We don't have the REST. What we need is that value of jv.ToString. You could make a 10 line MCVE for this. Learning how to debug is really your goal here. - David Heffernan
Well if you change your JSON it will work. {"Persons": <The array as standing above>} Works fine for me. - Teun Pronk
Thanks for your prompt answer David. I suppose you are talking about the example but what do you mean by 10 lines of MCVE ? the value of jv.ToString is shown already is the Json array - koul
do you think the json array need to be changed ? - koul
So why are you confusing us with all the REST? For all we know the REST doesn't work. - David Heffernan

2 Answers

1
votes

This could perfectly well be worked out by reading the code and looking at the JSON. However, I'd like to show you how to go about debugging such a problem in case you cannot work it out by static analysis. When an as cast fails that is always because the object on the left hand side of the as does not derive from the type on the right hand side. The next step then is always to inquire as to what the type of the object on the left hand side is. I've included a short MCVE above as a means to demonstrate.

The output of this program:

{$APPTYPE CONSOLE}

uses
  System.JSON;

const
  JSON = '[{"name":"Bryan","email":"[email protected]"},' +
         ' {"name":"Louis","email":"[email protected]"},' +
         ' {"name":"Maria","email":"[email protected]"},' +
         ' {"name":"Test","email":"[email protected]"},' +
         ' {"name":"Anthony","email":"[email protected]"}]';

begin
  Writeln(TJSONObject.ParseJSONValue(JSON).ClassName);
end.

is

TJSONArray

Now, TJSONArray does not derive from TJSONObject. Hence your as cast raise a runtime error. If you cast the value returned by ParseJSONValue to TJSONArray that will succeed.

This is to be expected since the root of your JSON is an array and is not an object.

You need to modify your code so that it does not assume that the root level is always an object. You need different behaviour for arrays and objects.

0
votes

I'm not sure what the problem of TJSONObject is with the string you posted.
For some reason it will parse it if you changed it.

{"Persons":[{"name":"Bryan","email":"[email protected]"},{"name":"Louis","email":"[email protected]"},{"name":"Maria","email":"[email protected]"},{"name":"Test","email":"[email protected]"},{"name":"Anthony","email":"[email protected]"}]}

If I run the code as it is I get the following result enter image description here

If you don't mind using something different than default Delphi units I would suggest superobject (Link here)
superobject will parse your JSON edited and as posted.

Your code would look like this:

Const
  MyJSON = '[{"name":"Bryan","email":"[email protected]"},{"name":"Louis","email":"[email protected]"},{"name":"Maria","email":"[email protected]"},{"name":"Test","email":"[email protected]"},{"name":"Anthony","email":"[email protected]"}]';    
procedure ParseJSON;
  var
    obj: ISuperObject;
    Ar: TSuperArray;
    I: integer;
  begin
    obj := SO(MyJSON);
    if obj.IsType(stArray) then
    begin
      Ar := obj.AsArray;
      try
        for I := 0 to Ar.Length-1 do
          L.Add(Ar.O[I].AsString);
      finally
        Ar.Free;
      end;
    end
    else
      L.Add(Obj.AsString);
  end;

Result:
enter image description here

For Koul, to get the element names and values.
Like I said not very pretty code but ok.

Ar.O[0].AsObject.GetNames.AsArray.S[0]

To cut it up in pieces a bit.

Ar.O[0] //Get the first element in the array as ISuperObject  
.AsObject //Get it as TSuperTableString
.GetNames //Gets all names in the array, in this case "name" and "email"
.AsArray[0]//Get the first name in the names array.

It will result in email (Names are sorted A-Z)

You can do the same for the values by calling GetValues instead of GetNames.
I think the prettiest way to get it will be defining 2x more TSuperArray

procedure PrintNamesAndValues;
Var
  Ar, ArNames, ArValues:TSuperArray;
  I: Integer;
begin
  Ar := SO(<JSON string>).asArray;  
  ArNames := Ar.O[0].AsObject.GetNames.AsArray;
  ArValues := Ar.O[0].AsObject.GetValues.AsArray;
  For I := 0 to ArNames.Length-1 do
    WriteLn(Format('%s: %s',[ArNames.S[I], ArValues.S[I]]));  
end;

Hope it's all clear enough :)