2012-07-16 54 views
8

晚上好!解析JSON到TListBox

我目前正試圖爲桌面組裝一個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's JSON Designtime ParserTiny-JSONLKJSON和內置的DBXJSON。但是,我完全沒有使用JSON的經驗,而且我似乎無法找到可以從中入手的最基本的示例。他們中的很多人都會顯示樣本數據,但我所嘗試過的所有人似乎都沒有像我期望的那樣工作,很可能是因爲我誤解了他們。我會假設組件能夠正常工作,所以我需要入門指導。

ips「數組」中有數百個(通常是數千個)結果(我道歉,如果這不正確,我會認爲它被稱爲數組,但是我對JSON完全陌生)。

我真正在尋找的是一些非常基本的示例代碼,我可以從中構建(以及它用於解析的組件等)。

例如,如果我想抓住每一個ip從JSON結果,並把每一個作爲一個單獨的項目爲TListBox(使用TListBox.add方法),我將如何去實現呢?

當我說ip,我的意思是價值(在格式化佈局上面,這將是xxx.xxx.xxx.xxxyyy.yyy.yyy.yyy)。另外,如果我想從JSON結果中找到IP的「記錄」(?)並將數據輸出到一個delphi數組中 - 例如:

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

這可能與JSON? (如果這被看作是一個單獨的問題或者太無關聯,請隨意編輯它,而不是將問題作爲一個整體)。

我得到這個最近已經不僅是IP的,但在一個單獨的TListItem(即responseipsipclassificationxxx.xxx.xxx.xxx所有其他數據片與一切有它自己的項目,有幾個空的項目一起在每個非空項目之間)。

我確定這個過程非常簡單,但是有很多關於JSON的信息,對於那些剛接觸這種格式的人來說,這是有點壓倒性的。

最好的問候, Scott Pritchard。

回答

8

一旦您瞭解了基本概念,JSON就非常簡單易懂。看看http://json.org,它解釋的東西。

有4個基本概念在JSON:

是任何JSON元件:一個基本的字符串或數字,數組或一個對象。 (決不是一個

一種陣列應該是熟悉的概念:值的有序列表。與Delphi數組的主要區別在於JSON數組沒有爲元素定義類型;他們只是「一組JSON值」。

A pair是一個鍵值對。該鍵可以是字符串或數字,並且該值可以是任何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; 

這一切只是示例代碼,當然,但它應該給你的東西的基礎上。

+0

非常感謝梅森!答案很好,我現在瞭解這些概念。我有點困惑,儘管我可能沒有在原始問題中解釋我自己(我曾經在開場白中說過,但在問題結束時很容易忘記)。 我應該提到,我已經在'TMemo'中獲得了純文本JSON(即通過API檢索到的未解析字符串),因此無法弄清楚如何將這個'lines.text'放入過程所需的'JSONObject'響應。另外,'ips:= pair.value as TJSONArray'返回'E2015 - 操作符不適用'。 – 2012-07-17 00:31:45

+1

@scott:看看'TJSONObject.Parse'。而該行應該使用'pair.JsonValue'來代替。我的錯。 – 2012-07-17 01:16:39

+0

爲了將來的參考(以及其他人正在尋找它),你需要在你的用例子句中包含'System.JSON'單元才能使這個例子工作 – 2015-09-22 12:00:16

1

編輯2: 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是簡單地含有未解析,明文JSON一個string(即,不是一個JSONString但實際的字符串)。

爲了保持連貫性,我已經將原始代碼留在了下面。


隨着早期的答案從梅森惠勒援助,以及由「TERAN」上question 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和唾手可得的迭代下來,並把它變成一個功能,我可以根據多個標準之一輸出任何數據或範圍的數據。爲了驗證我得到了正確數量的項目,我在最後添加了2個ShowMessage例程;一個用於列表框中的項目,另一個用於我解析的「ip」數據實例的數量。

該代碼在Firemonkey專門測試用的CloudFlare API JSON結果其是輸出到TMemo正是因爲他們被檢索(上&calls_left&a=zone_ips&class=t&geo=1 API調用,當然與zonetokenemail除了所附)。將它修改爲使用來自其他衆多API調用的其他結果應該相對容易。

爲了澄清,我沒有嘗試梅森的代碼,但不幸的是我無法得到它的工作。然而,我現在已經接受了他的答案,因爲他在基礎知識上的解釋是值得的,並幫助我達成最終解決方案,並提出我可以建立和教導自己的東西。