9

我正在尝试解析从 REST Web 服务返回的一些 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

这个问题不排除 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.
于 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 回答