2012-08-28 75 views
7

REST Webサービスから返されるJSONの一部を解析しようとしています。 get()呼び出しからの戻り値はTStringStreamです。 dbxjsonを使用してデータを処理しています。ここで簡単にデモを行うために、Webサービスを呼び出さずにエラーを再現するテストプロジェクトを作成しました(代わりにWebサービス出力のテキストファイルを使用します)。コードは次のとおりです。JSON空の配列

場合によっては、このJSONデータのphone_numbers配列が空です。

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

] 
     } 
    } 
} 

これにより、ParseJSONValueはnil値を返します。これにより、Webサービスコールからのストリームオブジェクトが生成されます。しかし

、私は私のテストtxtファイルで、これに空のphone_numbers配列変更した場合:

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

を、それが正常に動作します(つまりTJSONObjectが返されます)。違いは、空の配列の空白です。何らかの理由で、空の配列に空白を含む最初のJSON応答が、ParseJSONValueにnilを返します。角括弧の間に空白を入れずにうまく動作します。

JSON解析で何が問題になっていますか? ParseJSONValueを呼び出す前に何らかの事前解析が必要ですか?

+1

TJSONByteReaderの実装ではおそらくバグだと思われますが、率直に言って解析コードを理解しようとすると、一目でわかることができません。経験的に証拠はかなり明確です。 幸いにも自分のTJSONObjectリーダーがこのようなケースを完全にうまく処理します。おそらく公開する時間? :) – Deltics

+1

@Deltics:本当ですか?私はこの質問を調査しながらそれを追跡しましたが、解析コードを理解することが難しいとは思いませんでした。パーサーはひどく書かれていると思う - この問題は、レキシングを解析に混ぜるのではなく、適切なレクサーがあれば完全に回避されるだろうが、何が起こっているのかを理解することは難しくない... –

+0

PeekByte()は理解しやすいので、あなたは16進数で夢を見る必要があります。 :)私がJSONコードを公開すると、私が読める(私が保守可能と言っている)コードと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

関連する問題