Question

Good evening all.

I'm currently developing a cross-platform compatible version of my product WinFlare. The issue I'm facing is that SuperObject still isn't cross-platform compatible with Firemonkey. By all means, I used it in the original version of the product, but now I want to create a cross-platform version as opposed to one limited to just Windows, I'm finding it to be a hassle.

DBXJSON is the only cross-platform solution I've been able to find after extensive hours of research, but that's proving to be frustrating to try and deal with. Most all of the examples I've found for it either don't apply for my situation, or they're too complicated to gleam anything useful from. There's lots of discussion, but I'm just struggling to get to grips with what was such a simple task with SuperObject. I've spent the best part of this evening trying to find something that works to build from, but everything I've tried has just led me back to square one.

Ideally, I'd like to fix up SuperObject, but I lack the knowledge to go so in depth as to make it cross-platform compatible with OS X (and ready for the mobile studio). I'd welcome any suggestions on that, but as I imagine no one's got the time to go through such a huge task, it looks like DBXJSON is my only option.

The JSON layout I'm dealing with is still the same;

{
  response: {
    ips: [
       {
         ip: "xxx.xxx.xxx.xxx",
         classification: "threat",
         hits: xx,
         latitude: xx,
         longitude: xx,
         zone_name: "domain-example1"
         },
        {
         ip: "yyy.yyy.yyy.yyy",
         classification: "robot",
         hits: yy,
         latitude: xx,
         longitude: xx,
         zone_name: "domain-example2"
         }
       ]
   }
  result : "success",
  msg: null
}

There can be hundreds of results in the ips array. Let's say I want to parse through all of the items in the array and extract every latitude value. Let's also assume for a second, I'm intending to output them to an array. Here's the sort of code template I'd like to use;

procedure ParseJsonArray_Latitude(SInput : String);
var
  i : Integer;
  JsonArray : TJsonArray;
Begin
  // SInput is the retrieved JSON in string format
  { Extract Objects from array }

  for i := 0 to JsonArray.Size-1 do
  begin
    Array_Latitude[i] := JsonArray.Item[i].ToString;
  end;
end;

Essentially, where it says { Extract Objects from array }, I'd like the most basic solution using DBXJSON that would solve my problem. Obviously, the calls I've shown related to JsonArray in the template above might not be correct - they're merely there to serve as an aid.

Was it helpful?

Solution

First, parse the string to get an object.

var
  obj: TJsonObject;

obj := TJsonObject.ParseJsonValue(SInput) as TJsonObject;

That gives you an object with three attributes, response, result, and msg. Although ParseJsonValue is a method of TJsonObject, and your particular string input happens to represent an object value, it can return instances of any TJsonValue descendant depending on what JSON text it's given. Knowing that's where to start is probably the hardest part of working with DbxJson.

Next, get the response attribute value.

response := obj.Get('response').JsonValue as TJsonObject;

That result should be another object, this time with one attribute, ips. Get that attribute, which should have an array for a value.

ips := response.Get('ips').JsonValue as TJsonArray;

Finally, you can get the values from the array. It looks like you're expecting the values to be numbers, so you can cast them that way.

for i := 0 to Pred(ips.Size) do
  Array_Latitude[i] := (ips.Get(i) as TJsonObject).Get('latitude').JsonValue as TJsonNumber;

Remember to free obj, but not the other variables mentioned here, when you're finished.

OTHER TIPS

For completion, since the question stated that there was no alternative to DBXJSON for cross-platform, I would like to point out two Open Source alternatives, which appeared since the initial question.

SynCrossPlatformJSON is able to create schema-less objects or arrays, serialize and unserialize them as JSON, via a custom variant type, including late-binding to access the properties.

For your problem, you could write:

var doc: variant;
    ips: PJSONVariantData; // direct access to the array
    i: integer;
...
  doc := JSONVariant(SInput);   // parse JSON Input and fill doc custom variant type
  if doc.response.result='Success' then       // easy late-binding access
  begin
    ips := JSONVariantData(doc.response.ips); // late-binding access into array
    SetLength(Arr_Lat,ips.Count);
    for i := 0 to ips.Count-1 do begin
      Arr_lat[i] := ips.Values[i].latitude;
      Memo1.Lines.add(ips.Values[i].latitude); 
     end;
  end;  
... // (nothing to free, since we are using variants for storage)

Late-binding and variant storage allow pretty readable code.

Thanks to assistance from Rob Kennedy, I managed to build a solution that solved the problem;

var
  obj, response, arrayobj : TJSONObject;
  ips : TJSONArray;
  JResult : TJsonValue;
  i : Integer;
  Arr_Lat : Array of string;
begin
try
  Memo1.Lines.Clear;
  obj := TJsonObject.ParseJSONValue(SInput) as TJSONObject;
  response := Obj.Get('response').JsonValue as TJSONObject;
  ips := response.Get('ips').JsonValue as TJSONArray;
  SetLength(Arr_Lat, ips.Size-1);
  for i := 0 to ips.Size-1 do
    begin
      arrayobj := ips.Get(i) as TJSONObject;
      JResult := arrayobj.Get('latitude').JsonValue;
      Arr_lat[i] := JResult.Value;
      Memo1.Lines.Add(JResult.Value);
    end;
finally
  obj.Free;
end;

This will add the results to both the array (Arr_Lat), and output them to the memo (Memo1).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top