2012-08-28 434 views
7

我想解析從REST Web服務返回的一些JSON。 get()調用的返回是一個TStringStream。我使用dbxjson來處理數據。爲了使這裏更容易演示,我創建了一個測試項目,它在不調用Web服務的情況下重現錯誤(改爲使用Web服務輸出的文本文件)。以下是代碼: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; 

有時此JSON數據中的phone_numbers數組爲空。在來自Web服務調用的流對象中,它看起來像這樣:

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

] 
     } 
    } 
} 

這會導致ParseJSONValue返回一個零值。

但是,如果我改變了空PHONE_NUMBERS數組這在我的測試txt文件:

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

它工作正常(即返回TJSONObject)。不同之處在於空數組中的空白。出於某種原因,空數組中的第一個帶有空白的JSON響應會導致ParseJSONValue返回nil。它工作正常,方括號之間沒有空白。

我在做什麼錯我的JSON解析?在調用ParseJSONValue之前,我需要做某種預解析嗎?

+1

看起來這很可能是TJSONByteReader實現中的一個錯誤,但坦率地說,要理解解析代碼使得無法一目瞭然。經驗上證據是非常清楚的。 幸運的是我自己的TJSONObject閱讀器可以很好地處理這種情況。可能發佈時間? :) – Deltics

+1

@Deltics:真的嗎?我在調查這個問題的同時追溯了它,並且我沒有覺得難以理解解析代碼。我認爲解析器編寫得很糟糕 - 如果它有一個合適的詞法分析器,而不是將解析器中的文法混合在一起,這個問題就可以完全避免 - 但是不難理解正在發生什麼...... –

+0

如果您發現PeekByte()易於理解,那麼你必須在十六進制夢。 :)當我發佈我的JSON代碼時,你會看到我認爲可讀(我敢說可維護)代碼和um,dbxJSON之間的區別。 – Deltics

回答

8

這個問題不是Delphi JSON實現(DBXJSON)的專有問題,我使用了一些JSON PHP解析器,但是具有相同的限制。

現在因爲一個雙引號中的字符串字面量(且必須)由JSON解析器忽略以外的所有空格,你可以安全地刪除這些空格,那麼一個可能的解決方法是縮減大小您的JSON字符串,前解析它。

試試這個示例,該示例使用正則表達式從字符串中刪除多餘的空格。

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

看一看TJsonObject.ParseArray。你會發現這一點:

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

所以在陣列(它讀取開括號之後)的頂部,如果接下來的文字是不是閉括號,吃空白,然後嘗試讀取一個有效的JSON值。右括號不是有效的JSON值,因此它在此處保留。

這似乎是有效的JSON,(我可以讓我的瀏覽器接受它作爲一個有效的JavaScript對象),所以這應該被認爲是DBXJSON庫中的一個錯誤。你可能需要預先解析這個,使用一個不同的JSON庫(Delphi有一些),或者找到一種方法來確保發送給你的信息不包含這個模式。

無論採用哪種方式,您都應該將此報告給QC作爲錯誤。

+1

但是,即使XE2中已經支付了一項功能,但是您無法正確實施JSON規範,但無法在XE2中修復它。你忘了提及那部分。 – Deltics