2012-08-28 18 views
7

Estoy tratando de analizar un JSON que se devuelve desde un servicio web REST. El retorno de la llamada get() es un TStringStream. Estoy usando dbxjson para trabajar con los datos. Para hacer las cosas más fáciles de demostrar aquí, he creado un proyecto de prueba que reproduce el error sin llamar al servicio web (en su lugar, utiliza un archivo de texto para el resultado del servicio web). Aquí está el código:JSON matriz vacía

var SL : TStringStream; 
    LJsonObj : TJSONObject; 
begin 
    SL := TStringStream.Create; 
    try 
    SL.LoadFromFile('output.txt'); 
    LJsonObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(SL.DataString), 0) as TJSONObject; 
    finally 
    SL.Free; 
    end; 
end; 

veces la matriz numeros_telefono en estos datos JSON está vacía. En el objeto de secuencia procedente de la llamada de servicio web, se ve así:

{ 
    "Contact Information Service": { 
     "response": { 
      "phone_numbers": [ 

] 
     } 
    } 
} 

Esto hace que ParseJSONValue para devolver un valor nulo.

Sin embargo, si cambio de la matriz numeros_telefono en blanco a esta prueba en mi archivo txt:

{ 
    "Contact Information Service": { 
     "response": { 
      "phone_numbers": [] 
     } 
    } 
} 

funciona bien (es decir, devuelve un TJSONObject). La diferencia es el espacio en blanco en la matriz vacía. Por algún motivo, la primera respuesta JSON con espacios en blanco en la matriz vacía hace que ParseJSONValue devuelva nulo. Funciona bien, sin espacios en blanco entre los corchetes cuadrados.

¿Qué estoy haciendo mal con mi análisis JSON? ¿Hay algún tipo de pre-análisis que deba hacer antes de llamar a ParseJSONValue?

+1

Parece que es muy probable que sea un error en la implementación de TJSONByteReader, pero francamente tratando de dar sentido al código de análisis hace que sea imposible decirlo de un vistazo. Empíricamente la evidencia es bastante clara. Afortunadamente, mi propio lector TJSONObject maneja estos casos perfectamente. Es hora de publicar tal vez? :) – Deltics

+1

@Deltics: ¿De verdad? Lo rastreé mientras investigaba esta pregunta, y no me resultó tan difícil de entender el código de análisis.Creo que el analizador está mal escrito: este problema se habría evitado por completo si tuviera un léxer adecuado en lugar de mezclar el léxing con el análisis sintáctico, pero no es demasiado difícil entender lo que está pasando ... –

+0

Si has encontrado PeekByte() fácil de entender, entonces debes soñar en hex. :) Cuando publique mi código JSON verá la diferencia entre lo que considero código legible (me atrevo a decir que se puede mantener) y, um, dbxJSON. – Deltics

Respuesta

8

Este problema no es exclusivo de la implementación Delphi JSON (DBXJSON), trabajé con algunos analizadores JSON PHP con la misma limitación.

Ahora bien, porque todos los espacios en blanco fuera de un doble cadenas entre comillas literales son (y deben ser) ignorados por los analizadores JSON, puede eliminar estos espacios en blanco de manera segura, así que una posible solución es Minify su cadena JSON, antes para analizarlo

Pruebe esta muestra, que usa expresiones regulares para eliminar los espacios en blanco adicionales de una cadena.

{$APPTYPE CONSOLE} 

{$R *.res} 


uses 
    System.RegularExpressions, 
    System.Classes, 
    System.SysUtils, 
    Data.DBXJSON; 

const 
JsonString= 
'{'+ 
' "Contact Information Service": {'+ 
'  "response": {'+ 
'   "phone_numbers": [  ]'+ 
'  }'+ 
' }'+ 
'}'; 

function JsonMinify(const S: string): string; 
begin 
Result:=TRegEx.Replace(S,'("(?:[^"\\]|\\.)*")|\s+', '$1'); 
end; 

procedure TestJSon; 
var 
    s : string; 
    SL : TStringStream; 
    LJsonObj : TJSONObject; 
begin 
    SL := TStringStream.Create; 
    try 
    s:=JsonMinify(JsonString); 
    SL.WriteString(s); 
    LJsonObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(SL.DataString), 0) as TJSONObject; 
    Writeln(LJsonObj.Size); 
    finally 
    SL.Free; 
    end; 
end; 

begin 
try 
    TestJSon; 
except 
    on E:Exception do 
     Writeln(E.Classname, ':', E.Message); 
end; 
Writeln('Press Enter to exit'); 
Readln; 
end. 
7

Eche un vistazo a TJsonObject.ParseArray. Se encuentra esta:

while ValueExpected or (Br.PeekByte <> Ord(']')) do 
begin 
    ConsumeWhitespaces(Br); 
    Pos := ParseValue(Br, JsonArray); 
    if Pos <= 0 then 
    Exit(Pos); 

Así que en la parte superior de la matriz (inmediatamente después de que se lee el paréntesis abierto), si el siguiente carácter no es un paréntesis cerrado, come espacios en blanco y luego intenta leer un JSON válida valor. Un corchete estrecho no es un valor JSON válido, por lo que rescata en este punto.

Esto parece ser JSON válido, (puedo conseguir que mi navegador lo acepte como un objeto JavaScript válido), por lo que debería considerarse un error en la biblioteca DBXJSON. Es posible que deba realizar un análisis previo, utilizar una biblioteca JSON diferente (hay un puñado para Delphi) o encontrar la manera de asegurarse de que la información que se le envíe no contenga este patrón.

De cualquier manera, debe informar esto al control de calidad como un error.

+1

Pero no lo arreglarás en XE2 aunque es una falla clara y sencilla implementar correctamente la especificación JSON, una función que ya pagaste en XE2. Olvidaste mencionar esa parte. – Deltics