Parsing JSON into TListBox Parsing JSON into TListBox json json

Parsing JSON into TListBox


JSON is very simple and easy to figure out, once you understand the basic concepts. Have a look at http://json.org, where it explains things.

There are 4 basic concepts in JSON:

A value is any JSON element: a basic string or number, an array, or an object. (Anything but a pair.)

An array should be a familiar concept: an ordered list of values. The main difference from Delphi arrays is that JSON arrays don't have a defined type for the elements; they're simply "an array of JSON values."

A pair is a key-value pair. The key can be a string or a number, and the value can be any JSON value.

An object is an associative map of JSON pairs. You can think of it conceptually as a TDictionary<string, JSON value>.

So if I wanted to take a JSON array of data like that, and put it in a TListBox, I'd do something like this (DBXJSON example, warning: not tested):

procedure TMyForm.LoadListBox(response: TJSONObject);var  i: integer;  ips: TJSONArray;  ip: TJSONObject;  pair: TJSONPair;begin  ListBox.Clear;  pair := response.Get('ips');  if pair = nil then    Exit;  ips := pair.value as TJSONArray;  for i := 0 to ips.size - 1 do  begin    ip := ips.Get(i) as TJSONObject;    pair := ip.Get('ip');    if pair = nil then      ListBox.AddItem('???', ip.Clone)    else ListBox.AddItem(pair.JsonString, ip.Clone);  end;end;

Then you have a list of IP addresses, and associated objects containing the full record that you can get at if the user selects one. (If you wanted to put the entire contents of each record into the list control, have a look at TListView. It works better than TListBox for that.)

And if you want to build an array of strings containing all the values, do something like this:

function JsonObjToStringArray(obj: TJsonObject): TArray<string>;var  i: integer;begin  SetLength(result, obj.Size);  for i := 0 to obj.Size - 1 do    result[i] := obj.Get(i).JsonValue.ToString;end;

This is all just sample code, of course, but it should give you something to build on.


EDIT2: AV Fixed with extreme ease.

EDIT: After further examining my own code, i realised it would cause a massive amount of memory leaks. However, i have since switched over to SuperObject and found the same result can be achieved in 2 lines of code with only 2 variables and no memory leaks;

Procedure ParseIPs;  ISO : ISuperObject;  MyItem : ISuperObject;begin  ISO := SO(RetrievedJSON);  for MyItem in ISO['response.ips'] do Memo2.Lines.Add(MyItem.S['ip']);end;

RetrievedJSON is simply a string containing the unparsed, plaintext JSON (i.e. not a JSONString but an actual string).

I've left the original code underneath for sake of continuity.


With assistance from Mason Wheeler in an earlier answer, as well as an answer provided by "teran" on question 9608794, i successfully built the following to parse down to the actual level (i.e. the "array" containing the data) i needed to access, and then output all items with a specific JSONString.Value into a listbox (named LB1 in the sample below);

Procedure ParseIP;var  o, Jso, OriginalObject : TJSONObject;  ThePair, JsPair : TJSONPair;  TheVal, jsv : TJSONValue;  jsArr : TJsonArray;  StrL1 : String;  i, num : Integer;begin  num := 0;  o := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Memo1.Text), 0) as TJSONObject;  ThePair := o.Get('response');  TheVal := ThePair.JsonValue;  STRL1 := TheVal.ToString;  JSV := TJSONObject.ParseJSONValue(STRL1);  OriginalObject := JSV as TJSONObject;  JSPair := OriginalObject.Get('ips');  JSARR := JSPair.JsonValue as TJSONArray;  for i := 0 to JsArr.Size-1 do    begin      JSO := JSArr.Get(i) as TJSONObject;      for JSPAIR in JSO do        begin        num := num+1;          if JSPAIR.JsonString.Value = 'ip' then          begin            LB1.Items.Add(JSPair.JsonValue.Value);          end          else null;        end;    end;    ShowMessage('Items in listbox: ' + IntToStr(LB1.Items.Count));    ShowMessage('Items in JSON: ' + IntToStr(num div JSO.Size));    Jsv.Free;end;

While this is an extremely round-about way of doing it, it allows me to look at each individual step, and see where it's iterating down through the JSON and with extreme ease, and to change it into a function where i can output any piece or range of data as a result based on one of multiple criteria. For the sake of verifying i got the correct number of items, i added 2 ShowMessage routines at the end; One for the items in the listbox, and one for the number of instances of "ip" data that i was parsing.

This code was specifically tested in Firemonkey with CloudFlare API JSON results which were output into a TMemo exactly as they were retrieved (on an &calls_left&a=zone_ips&class=t&geo=1 API call, of course with your zone, token and email appended in addition). It should be relatively easy to modify it to work with other results from the numerous other API calls too.

To clarify, i did try Mason's code, but unfortunately i couldn't get it working. However, i have accepted his answer for the time being on the basis that the explanation he gave on the basics was worthy of it and assisted me in getting to an end-solution and coming up with something i can build from and teach myself.