晚上好人!
我目前正在尝试为桌面组建一个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
}
我尝试了几个不同的组件 - SuperObject,Paweł Głowacki's JSON Designtime Parser,Tiny-JSON,LKJSON和内置的DBXJSON。但是,我根本没有使用JSON的经验,我似乎无法找到可以从中开始的最基本的示例。他们中的许多人都展示了样本数据,但我尝试的所有数据似乎都没有像我期望的那样工作,很可能是因为我误解了它们。我假设组件有效,所以我需要有关入门的指导。
ips
“数组中有数百个,通常是数千个结果”(如果这不正确,我会道歉,我认为它被称为数组,但我又是JSON的新手)
我真正需要的是一些非常基本的示例代码,我可以构建它(以及它用于解析等的组件)。
例如,如果我想从JSON结果中抓取每个ip
,并将每个{@ 1}}作为单独的项目放入TListBox
(使用TListBox.add
方法),我将如何实现这一目标?
当我说ip
时,我的意思是价值(在上面的格式化布局中,这将是xxx.xxx.xxx.xxx
或yyy.yyy.yyy.yyy
)。
此外,如果我想通过JSON结果中的IP找到“记录”(?)并将数据输出到delphi数组 - 例如;
Result : Array of String = ['"xxx.xxx.xxx.xxx"','"threat"','xx','null','null','"domain-example1"'];
可以使用JSON吗? (如果这被视为一个单独的问题或者过于无关,请随意编辑,而不是整体关闭问题。)
我得到的最接近的不仅是ip,而且是单独的TListItem
中的所有其他数据(即response
,ips
,ip
,{ {1}},classification
以及其他所有内容都有自己的项目,以及每个非空项目之间的几个空项目。)
我确信这样做非常简单,但是关于JSON的信息非常多,对于那些刚接触该格式的人来说,它有点压倒性。
最诚挚的问候, Scott Pritchard。
答案 0 :(得分: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;
当然,这只是示例代码,但它应该为您提供一些可以构建的东西。
答案 1 :(得分: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的帮助下,在早期的回答中,以及question 9608794上的“teran”提供的答案,我成功构建了以下内容以解析实际级别(即包含数据)我需要访问,然后将具有特定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中迭代的位置,并将其更改为我可以的功能根据多个标准之一输出任何一个或一系列数据。为了验证我得到了正确数量的项目,我最后添加了2个ShowMessage
例程;一个用于列表框中的项目,另一个用于我正在解析的“ip”数据的实例数。
此代码在Firemonkey中经过专门测试,其中包含CloudFlare API JSON结果,这些结果与TMemo
完全相同(在&calls_left&a=zone_ips&class=t&geo=1
API调用时输出到zone
,当然还有token
,email
和{{1}}另外附加)。修改它以便与其他许多API调用的其他结果一起使用也应该相对容易。
为了澄清,我确实尝试过Mason的代码,但不幸的是我无法让它运转起来。然而,我暂时接受了他的答案,理由是他对基础知识的解释是值得的,并帮助我找到最终解决方案并提出我可以建立并自学的东西。< / p>