8

晚上好,伙计们!

我目前正在尝试CloudFlare为桌面组装一个客户端。我已连接到他们的 API 并通过 POST 请求成功检索了 JSON 结果(其结果已输出到TMemo. 我现在想将这些结果解析成一个TListBox(例如,参见粗体区域)。该项目正在设计中Firemonkey

这是带有一些示例内容的响应的格式化布局;

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

我尝试了几种不同的组件——SuperObjectPaweł Głowacki 的 JSON Designtime ParserTiny-JSONLKJSON和内置的 DBXJSON。但是,我根本没有使用 JSON 的经验,而且我似乎找不到可以开始使用的最基本的示例。其中许多显示了示例数据,但我尝试过的所有数据似乎都没有按我的预期工作,很可能是因为我误解了它们。我假设这些组件可以工作,所以我需要入门指导。

“数组”中有数百个,通常是数千个结果ips(如果这不正确,我深表歉意,我假设它被称为数组,但同样,我对 JSON 完全陌生)。

我真正在寻找的是某种非常基本的示例代码,我可以从中构建(以及它用于解析的组件等)。

例如,如果我想ip从 JSON 结果中获取每一项,并将每一项作为单独的项目放入TListBox(使用TListBox.add方法)中,我将如何实现这一点?

当我说 时ip,我的意思是值(在上面的格式化布局中,这将是xxx.xxx.xxx.xxxor yyy.yyy.yyy.yyy)。

此外,如果我想通过它的 IP 从 JSON 结果中找到“记录”(?)并将数据输出到 delphi 数组 - 例如;

Result : Array of String = ['"xxx.xxx.xxx.xxx"','"threat"','xx','null','null','"domain-example1"'];

JSON可以吗?(如果这被视为一个单独的问题或太不相关,请随时编辑它而不是关闭整个问题)。

我得到的最接近的不仅是ip,而且是单独的所有其他数据(TListItemresponse,,,,ips以及其他所有内容都有它自己的项目ip,以及每个非空项目之间的几个空项目)。classificationxxx.xxx.xxx.xxx

我确信这非常简单,但是关于 JSON 的信息太多了,对于刚接触这种格式的人来说有点不知所措。

最好的问候,斯科特·普里查德。

4

2 回答 2

8

一旦您了解了基本概念,JSON 非常简单且易于理解。看看http://json.org,它解释了一些事情。

JSON中有4个基本概念:

值是任何 JSON 元素:基本字符串或数字、数组或对象。(除了一之外的任何东西。)

数组应该是一个熟悉的概念:值的有序列表。与 Delphi 数组的主要区别在于 JSON 数组没有定义元素的类型。它们只是“一组 JSON 值”。

是键值对。键可以是字符串或数字,值可以是任何 JSON 值。

对象是 JSON 对的关联映射。您可以在概念上将其视为TDictionary<string, JSON value>.

因此,如果我想获取这样的 JSON 数据数组,并将其放入 TListBox,我会做这样的事情(DBXJSON 示例,警告:未测试):

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;

然后,您有一个 IP 地址列表,以及包含完整记录的关联对象,如果用户选择一个,您可以获得这些记录。(如果您想将每条记录的全部内容放入列表控件中,请查看TListView。它比这样做更好TListBox。)

如果您想构建一个包含所有值的字符串数组,请执行以下操作:

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;

当然,这只是示例代码,但它应该为您提供一些构建基础。

于 2012-07-16T23:01:38.357 回答
1

EDIT2: AV 非常轻松地修复。

编辑:在进一步检查我自己的代码后,我意识到它会导致大量的内存泄漏。但是,我已经切换到SuperObject并发现可以在 2 行代码中实现相同的结果,只有 2 个变量并且没有内存泄漏;

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只是一个string包含未解析的纯文本 JSON(即不是一个JSONString而是一个实际的字符串)。

为了连续性,我将原始代码留在了下面。


在 Mason Wheeler 较早的回答中以及“teran”在问题 9608794上提供的回答的帮助下,我成功构建了以下内容以解析到我需要访问的实际级别(即包含数据的“数组”) ,然后将具有特定的所有项目输出JSONString.Value到列表框中(在下面的示例中命名LB1);

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;

虽然这是一种非常迂回的方法,但它允许我查看每个单独的步骤,并非常轻松地查看它在 JSON 中向下迭代的位置,并将其更改为我可以输出任何片段的函数或数据范围作为基于多个标准之一的结果。为了验证我得到了正确数量的项目,我ShowMessage在最后添加了 2 个例程;一个用于列表框中的项目,一个用于我正在解析的“ip”数据实例的数量。

此代码在 Firemonkey 中使用 CloudFlare API JSON 结果进行了专门测试,这些结果与TMemo检索到的结果完全相同(在&calls_left&a=zone_ips&class=t&geo=1API 调用中,当然还有您的zone,tokenemail附加)。修改它以使用来自众多其他 API 调用的其他结果也应该相对容易。

为了澄清,我确实尝试了 Mason 的代码,但不幸的是我无法让它工作。但是,我暂时接受了他的回答,因为他对基础知识的解释是值得的,并帮助我找到了最终解决方案,并提出了一些我可以从中构建和自学的东西。

于 2012-07-17T17:55:14.580 回答