2012-08-28 18 views
7

Próbuję przeanalizować niektóre JSON, które są zwracane z usługi sieciowej REST. Zwrot z wywołania get() to TStringStream. Używam dbxjson do pracy z danymi. Aby ułatwić tutaj prezentację, utworzyłem projekt testowy, który odtwarza błąd bez wywoływania usługi sieciowej (zamiast tego używa pliku tekstowego do wyświetlenia usługi WWW). Oto kod:Pusta tablica JSON

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; 

Czasami tablica phone_numbers w tym danych JSON jest pusty. W obiekcie Strumień pochodzący z wzywania serwisu www, wygląda to tak:

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

] 
     } 
    } 
} 

Powoduje ParseJSONValue zwrócić wartość zero.

Jednak jeśli zmienię pusty phone_numbers tablicę do tego w moim testowym pliku txt:

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

działa dobrze (tzn zwraca TJSONObject). Różnica jest białą spacją w pustej tablicy. Z jakiegoś powodu pierwsza odpowiedź JSON z białymi znakami w pustej tablicy powoduje, że ParseJSONValue zwróci zero. Działa dobrze bez białych spacji między nawiasami kwadratowymi.

Co robię źle z moim parsowania JSON? Czy jest jakiś rodzaj wstępnego parsowania, które muszę wykonać przed wywołaniem ParseJSONValue?

+1

Wygląda na to, że to najprawdopodobniej błąd w implementacji TJSONByteReader, ale szczerze mówiąc, próbując zrozumieć kod parsujący, nie można tego stwierdzić na pierwszy rzut oka. Empirycznie dowody są dość jasne. Na szczęście mój własny czytnik TJSONObject doskonale radzi sobie z takimi przypadkami. Czas na opublikowanie? :) – Deltics

+1

@Deltics: Naprawdę? Przeszukałem to podczas badania tego pytania i nie było mi trudno zrozumieć kod parsujący.Wydaje mi się, że parser jest źle napisany - problem ten zostałby całkowicie wyeliminowany, gdyby miał odpowiedni lexer zamiast mieszać leksyk z analizą - ale nie jest zbyt trudno zrozumieć, co się dzieje ... –

+0

Jeśli znalazłeś PeekByte() łatwe do zrozumienia, musisz śnić na heksie. :) Kiedy opublikuję mój kod JSON, zobaczysz różnicę między tym, co uważam za czytelny (odważ się twierdzić, że można to naprawić) a um, dbxJSON. – Deltics

Odpowiedz

8

Ten problem nie dotyczy wyłącznie implementacji Delphi JSON (DBXJSON), pracowałem z niektórymi parserami PHP JSON z tym samym ograniczeniem.

Teraz bo wszystkie wykroje poza podwójnymi cudzysłowami literały są (i muszą) być ignorowane przez parsery JSON, można usunąć te spacji bezpiecznie, więc możliwe obejście jest Minify ciąg JSON przed parsować to.

Wypróbuj tę próbkę, która używa wyrażeń regularnych w celu usunięcia dodatkowych białych znaków z ciągu.

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

Spójrz na TJsonObject.ParseArray. Znajdziesz to:

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

więc w górnej części tablicy (zaraz po odczytuje otwarty nawias kwadratowy), jeśli następny znak nie jest blisko wspornik, jeść białe znaki, a następnie spróbuj odczytać poprawną JSON wartość. Klamra zamykająca nie jest poprawną wartością JSON, więc w tym momencie jest wysyłana.

Wygląda na to, że jest to poprawny JSON, (mogę ustawić przeglądarkę, aby zaakceptowała go jako prawidłowy obiekt JavaScript), więc należy to uznać za błąd w bibliotece DBXJSON. Może zajść potrzeba wstępnego przeanalizowania tego, skorzystania z innej biblioteki JSON (jest kilka dla Delphi) lub znalezienia sposobu, aby upewnić się, że informacje przesyłane do ciebie nie zawierają tego wzorca.

Tak czy inaczej, powinieneś zgłosić to QC jako błąd.

+1

Ale nie zostanie to naprawione w XE2, mimo że jest to jasne i proste niepowodzenie we właściwym wprowadzeniu specyfikacji JSON - funkcja, którą już zapłaciłeś w XE2. Zapomniałeś wspomnieć o tej części. – Deltics

Powiązane problemy