Пустой массив JSON

Я пытаюсь проанализировать некоторые JSON, которые возвращаются из веб-службы REST. Возврат из вызова get() является TStringStream. Я использую dbxjson для работы с данными. Чтобы упростить демонстрацию здесь, я создал тестовый проект, который воспроизводит ошибку без вызова веб-службы (вместо этого использует текстовый файл для вывода веб-службы). Вот код:

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;

иногда массив phone_numbers в этих данных JSON пуст. В объекте потока из вызова веб-службы это выглядит следующим образом:

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

]
        }
    }
}

Это заставляет ParseJSONValue возвращать значение nil.

однако, если я изменю пустой массив phone_numbers на это в моем тестовом txt-файле:

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

он отлично работает (т. е. возвращает TJSONObject). Разница заключается в пробелах в пустом массиве. По какой-то причине первый ответ JSON с пробелами в пустом массиве заставляет ParseJSONValue возвращать nil. Он отлично работает без пробелов между квадратными скобами.

что я делаю неправильно с моим разбором JSON? Есть ли какой-то предварительный анализ, который мне нужно сделать перед вызовом ParseJSONValue?

2 ответов


эта проблема не является исключительной для реализации Delphi JSON (DBXJSON), я работал с некоторыми парсерами PHP JSON с тем же ограничением.

теперь, поскольку все пробелы вне литералов строк с двойными кавычками (и должны быть) игнорируются синтаксическими анализаторами JSON, вы можете безопасно удалить эти пробелы, поэтому возможным обходным путем является Minify ваша строка 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+', '');
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.

посмотреть 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 как об ошибке.