2012-07-16 41 views
8

Buenas tardes chicos!Análisis JSON en TListBox

Actualmente estoy tratando de armar un cliente CloudFlare para el escritorio. Me he conectado a su API y recuperé con éxito los resultados de JSON con una solicitud POST (cuyos resultados se han generado en TMemo). Ahora quiero analizar estos resultados en un TListBox (ver el área en negrita, por ejemplo). El proyecto está siendo diseñado en Firemonkey.

Aquí está el diseño formateado de la respuesta con algunos ejemplos de contenido;

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

que he probado varios componentes diferentes - SuperObject, Paweł Głowacki's JSON Designtime Parser, Tiny-JSON, LKJSON y el construido en DBXJSON. Sin embargo, no tengo ninguna experiencia con JSON en absoluto y parece que no puedo encontrar los ejemplos más básicos de los que puedo partir. Muchos de ellos muestran datos de muestra, pero todos los que he probado no parecen funcionar como esperaba, muy probablemente porque los estoy malinterpretando. Asumiría que los componentes funcionan, entonces necesito una guía para comenzar.

Hay cientos, a menudo miles, de resultados en el ips "matriz" (me disculpo si eso no es correcto, supongo que se conoce como una matriz, pero de nuevo, soy completamente nuevo en JSON).

Lo que realmente estoy buscando es algún tipo de código de muestra extremadamente básico que pueda compilar (junto con qué componente usa para analizar y demás).

Por ejemplo, si quería aprovechar cada ip partir de los resultados JSON, y poner a cada uno como un punto separado en un (TListBox.add método que usa) TListBox, ¿cómo hago para lograr esto?

Cuando digo ip, quiero decir el valor (en el diseño de formato anterior, esto sería xxx.xxx.xxx.xxx o yyy.yyy.yyy.yyy).

Además, si quería encontrar un "registro" (?) Por su IP a partir de los resultados JSON y enviar los datos a una matriz delphi - por ejemplo;

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

¿Eso es posible con JSON? (Si esto se ve como una pregunta separada o sin relación, no dude en editarlo en lugar de cerrar la pregunta como un todo).

lo más cerca que llegué a este tenía no sólo los de IP, pero cualquier otra pieza de datos en particular TListItem (es decir response, ips, ip, classification, xxx.xxx.xxx.xxx y todo lo demás tenía su propio tema, junto con varios elementos vacíos entre cada elemento no vacío).

Estoy seguro de que es extremadamente simple de hacer, pero hay tanta información en JSON que es un poco abrumador para las personas nuevas en el formato.

Saludos cordiales, Scott Pritchard.

Respuesta

8

JSON es muy simple y fácil de entender, una vez que comprende los conceptos básicos. Eche un vistazo a http://json.org, donde explica las cosas.

Hay 4 conceptos básicos en JSON:

A valor es cualquier elemento JSON: una cadena de base o un número, una matriz o un objeto. (. Cualquier cosa menos un par)

Una gama debe ser un concepto familiar: una lista ordenada de valores. La diferencia principal de las matrices Delphi es que las matrices JSON no tienen un tipo definido para los elementos; simplemente son "una matriz de valores JSON".

A par es un par clave-valor. La clave puede ser una cadena o un número, y el valor puede ser cualquier valor JSON.

Un objeto es un mapa asociativo de pares JSON. Puedes pensarlo conceptualmente como TDictionary<string, JSON value>.

Así que si quería tomar una matriz JSON de los datos por el estilo, y lo puso en un TListBox, me gustaría hacer algo como esto (ejemplo DBXJSON, la advertencia: no probado):

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; 

Entonces tiene una lista de direcciones IP y objetos asociados que contienen el registro completo al que puede acceder si el usuario selecciona uno. (Si desea poner todo el contenido de cada registro en el control de lista, eche un vistazo a TListView. Funciona mejor que TListBox para eso.)

Y si desea construir una matriz de cadenas que contengan todos los valores , hacer algo como esto:

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; 

esto es todo código sólo muestra, por supuesto, pero hay que darle algo para construir.

+0

Muchas gracias Mason! La respuesta es genial y ahora entiendo los conceptos. Estoy un poco confundido, aunque tal vez no me explique lo suficiente en la pregunta original (lo dije en el primer partido, pero es fácil olvidarlos al final de la pregunta). Debo mencionar que tengo el texto plano JSON (es decir, una cadena no analizada recuperada a través de la API) en un 'TMemo' y, como tal, no puedo encontrar la forma de colocar' lines.text' de esto en el 'JSONObject' respuesta requerida por el procedimiento. Además, 'ips: = pair.value as TJSONArray' devuelve' E2015 - Operator not applicable'. –

+1

@scott: Eche un vistazo a 'TJSONObject.Parse'. Y esa línea probablemente debería usar 'pair.JsonValue' en su lugar. Mi error. –

+0

Para referencia futura (y para cualquier otra persona que lo busque), debe incluir la unidad 'System.JSON' en su cláusula uses para que el ejemplo funcione –

1

EDIT2: AV Se corrigió con extrema facilidad.

EDIT: Después de examinar mi propio código, me di cuenta de que causaría una gran cantidad de pérdidas de memoria. Sin embargo, desde entonces cambié a SuperObject y encontré que el mismo resultado se puede lograr en 2 líneas de código con solo 2 variables y sin pérdida de memoria;

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 es simplemente una string que contiene el, JSON de texto claro no analizada (es decir, no un JSONString pero una cadena real).

He dejado el código original debajo para mayor continuidad.


Con la ayuda de Mason Wheeler en una respuesta anterior, así como una respuesta proporcionada por "Terán" en question 9608794, he construido con éxito el siguiente para analizar hasta el nivel real (es decir,la "matriz" que contiene los datos) necesitaba acceder, y luego mostrar todos los elementos con un JSONString.Value específico en un cuadro de lista (llamado LB1 en el ejemplo siguiente);

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; 

Si bien esta es una manera acerca de ida y extremadamente de hacerlo, que me permite mirar a cada paso, y ver dónde se iterar a través de la JSON y con extrema facilidad, y para convertirla en una función donde puedo generar cualquier pieza o rango de datos como resultado en base a uno de múltiples criterios. Por el bien de verificar que obtuve el número correcto de artículos, agregué 2 ShowMessage rutinas al final; Uno para los elementos en el cuadro de lista, y otro para el número de instancias de datos "ip" que estaba analizando.

Este código se probó específicamente en FireMonkey con resultados CloudFlare API JSON que eran salida en un TMemo exactamente como fueron recuperados (en una llamada &calls_left&a=zone_ips&class=t&geo=1 API, por supuesto con su zone, token y email adjunto, en adición). Debería ser relativamente fácil modificarlo para que funcione con otros resultados de las numerosas otras llamadas API también.

Para aclarar, probé el código de Mason, pero desafortunadamente no pude hacerlo funcionar. Sin embargo, he aceptado su respuesta por el momento sobre la base de que la explicación que dio sobre los conceptos básicos fue digna de ella y me ayudó a llegar a una solución final y encontrar algo de lo que pueda construir y enseñarme a mí mismo.