9

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

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はnil値を返します。

ただし、テストtxtファイルで空のphone_numbers配列をこれに変更すると、次のようになります。

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

正常に動作します(つまり、TJSONObjectを返します)。違いは、空の配列の空白です。何らかの理由で、空の配列に空白を含む最初のJSON応答により、ParseJSONValueはnilを返します。中括弧の間に空白がなくても問題なく動作します。

JSON解析で何が間違っていますか?ParseJSONValueを呼び出す前に実行する必要があるある種の事前解析はありますか?

4

2 に答える 2

10

この問題はDelphiJSON実装(DBXJSON)に限ったことではなく、同じ制限のあるいくつかのJSONPHPパーサーを使用しました。

二重引用符で囲まれた文字列リテラルの外側のすべての空白は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.
于 2012-08-29T01:51:03.390 に答える
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に報告する必要があります。

于 2012-08-29T00:05:25.010 に答える