跨平台JSON解析

时间:2013-02-03 23:31:41

标签: json delphi delphi-xe3 firemonkey-fm2

晚上好。

我目前正在开发我的产品WinFlare的跨平台兼容版本。我面临的问题是SuperObject仍然不与Firemonkey跨平台兼容。无论如何,我在产品的原始版本中使用它,但现在我想创建一个跨平台版本,而不是仅限于Windows,我发现这是一个麻烦。

DBXJSON是我经过长时间研究后才能找到的唯一跨平台解决方案,但事实证明这是令人沮丧的尝试和处理。我发现的大部分例子都不适用于我的情况,或者它们太复杂,无法从中汲取任何有用的东西。有很多讨论,但我只是在努力去掌握SuperObject这么简单的任务。我今晚花了大部分时间试图找到可以构建的东西,但是我尝试过的所有东西都让我回到原点。

理想情况下,我想修复SuperObject,但我缺乏深入的知识,以使其与OS X跨平台兼容(并为移动工作室做好准备)。我欢迎任何有关这方面的建议,但正如我想象的那样,没有人有时间完成这么大的任务,看起来DBXJSON是我唯一的选择。

我正在处理的JSON布局仍然相同;

{
  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
}

ips数组中可能有数百个结果。假设我想解析数组中的所有项并提取每个latitude值。让我们假设一秒钟,我打算将它们输出到一个数组。这是我想要使用的那种代码模板;

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;

基本上,在{ Extract Objects from array }的位置,我希望最基本的解决方案使用 DBXJSON 来解决我的问题。显然,我在上面的模板中显示的与JsonArray相关的电话可能不正确 - 它们只是作为一种帮助。

3 个答案:

答案 0 :(得分:3)

首先,解析字符串以获取对象。

var
  obj: TJsonObject;

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

它为您提供了一个具有三个属性的对象,响应,结果和消息。虽然ParseJsonValueTJsonObject的方法,并且您的特定字符串输入恰好表示对象值,但它可以返回任何TJsonValue后代的实例,具体取决于它给出的JSON文本。知道从哪里开始可能是使用DbxJson最难的部分。

接下来,获取响应属性值。

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

该结果应该是另一个对象,这次使用一个属性ips。获取该属性,该属性应具有值的数组。

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

最后,您可以从数组中获取值。看起来你期望值是数字,所以你可以这样投射它们。

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

当你完成时,请记得免费obj,而不是这里提到的其他变量。

答案 1 :(得分:2)

完成之后,由于问题表明跨平台没有DBXJSON的替代方案,我想指出两个开源替代方案,这些方案自最初的问题出现以来。

SynCrossPlatformJSON能够创建无架构对象或数组,通过自定义variant类型将它们序列化并反序列化为JSON,包括后期绑定访问属性。

对于你的问题,你可以写:

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)

后期绑定和变体存储允许非常易读的代码。

答案 2 :(得分:0)

感谢Rob Kennedy的帮助,我设法构建了解决问题的解决方案;

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;

这会将结果添加到数组(Arr_Lat),并将它们输出到备忘录(Memo1)。