晚上好伙计们!
我目前正在尝试为桌面组装一个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 TListBox ,并将其作为一个单独的项放入TListBox(使用 TListBox.add 方法),那么我将如何实现?
当我说ip时,我指的是值(在上面格式化的布局中,这将是xxx.xxx.xxx.xxx或yyy.yyy.yyy.yyy)。
另外,如果我想找一张“唱片”(?)通过它的IP从JSON的结果,并输出数据到一个delphi数组-例如;
Result : Array of String = ['"xxx.xxx.xxx.xxx"','"threat"','xx','null','null','"domain-example1"'];用JSON可以吗?(如果这是一个单独的问题或过于无关的问题,请随意编辑,而不是将问题作为一个整体关闭)。
我最近接触到的不仅仅是ip,还有一个独立的TListItem中的其他数据段(即response、ips、ip、classification、xxx.xxx.xxx.xxx和其他所有东西都有它自己的项,以及每个非空项之间的几个空项)。
我相信这是非常简单的,但是关于JSON的信息太多了,对于刚开始使用JSON格式的人来说,这有点让人难以接受。
祝你好运斯科特·普里查德。
发布于 2012-07-16 23:01:38
一旦您理解了基本概念,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地址列表,以及包含如果用户选择一个完整记录的关联对象。(如果要将每个记录的全部内容放入list控件,请查看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-17 17:55:14
EDIT2: AV非常容易修复。
编辑:在进一步检查我自己的代码之后,我意识到它会导致大量内存泄漏。然而,我已经切换到SuperObject,并发现同样的结果可以在两行代码中实现,只有两个变量,没有内存泄漏;
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只是一个包含未解析的明文JSON的string (即不是JSONString,而是实际的字符串)。
为了保持连续性,我把原始代码留在了下面。
在梅森惠勒的帮助下,在较早的答案中,以及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中的迭代位置,并非常容易地将其转换为一个函数,在这个函数中,我可以根据多个条件之一输出任何数据。为了验证我得到了正确的项目数,我在末尾添加了两个ShowMessage例程;一个用于列表框中的项,一个用于解析的"ip“数据实例数。
这段代码在Fire猴子中使用CloudFlare addition结果进行了专门测试,这些结果被输出到TMemo中,与检索它们完全一样(当然是在&calls_left&a=zone_ips&class=t&geo=1 API调用中,当然是附加了zone、token和email )。修改它以处理来自其他API调用的其他结果也应该相对容易一些。
为了澄清,我确实尝试过Mason的代码,但不幸的是,我无法让它工作。然而,我暂时接受了他的回答,因为他对基本知识所作的解释是值得的,并帮助我找到了一个最终的解决方案,并想出了一些我可以从中建立起来并教会自己的东西。
https://stackoverflow.com/questions/11511488
复制相似问题