Требования к синтаксическому анализатору JSON

Я думаю о реализации парсера JSON для Delphi. Что должен делать хороший парсер JSON? Любые идеи для требований? Я считаю, что он должен по крайней мере выводить и обрабатывать JSON... Глядя на синтаксические анализаторы XML, должен ли он быть больше похож на DOM или SAX?


person pglowack    schedule 05.01.2009    source источник


Ответы (8)


Я думаю, что Json.NET отлично справляется со своей задачей.

  • JsonReader и JsonWriter для низкоуровневой обработки и вывода JSON.
  • JsonSerializer для преобразования объектов в JSON и обратно
  • Классы JObject, JArray и JValue для работы с JSON в объектной модели

Обратите внимание, что я могу быть немного предвзятым, так как я написал это :)

person James Newton-King    schedule 09.01.2009
comment
Абсолютно - разделение между потоковым/инкрементным синтаксическим анализатором/генератором и высокоуровневыми полными конструкциями в памяти является обязательным, на мой взгляд. - person StaxMan; 22.10.2010
comment
Это модульный подход DBXJSON. - person Arnaud Bouchez; 05.06.2011

Хорошим основанием будет вся функциональность, предоставляемая следующими тремя (взято с JSON.org): uJson, инструментарий JSON и lkjson.

person Hank Gay    schedule 05.01.2009

Я использовал набор инструментов JSON в нескольких проектах с большим успехом. Единственное, что я изменил в какой-то момент, это то, как он форматирует результирующий JSON, но это вопрос личного вкуса.

Он бесплатный, довольно чистый и простой в использовании. Нет необходимости устанавливать пакеты; просто есть файл .pas где-то на вашем пути. Просто проверьте test_usage.dpr ниже, чтобы увидеть несколько простых примеров того, как его использовать. Это не становится намного проще, чем это.

Я бы не стал тратить время на реализацию еще одного анализатора JSON, если только вы не хотите делать это в образовательных целях, и в этом случае вам все равно следует внимательно изучить существующие реализации.

Главная страница JSON Toolkit: http://www.progdigy.com/?page_id=6

program test_usage;
{$IFDEF FPC}
  {$MODE OBJFPC}{$H+}
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}

uses
  SysUtils,
  superobject;

var
  my_string, my_int, my_object, my_array: ISuperObject;
  new_obj: ISuperObject;
  j: integer;
  ite: TSuperObjectIter;

begin
  try
    my_string := TSuperObject.Create(#9);
    writeln('my_string=', my_string.AsString);
    writeln('my_string.AsJSon=', my_string.AsJSon);

    my_string := TSuperObject.Create('foo');
    writeln('my_string=', my_string.AsString);
    writeln('my_string.AsJson=', my_string.AsJson);

    my_int := TSuperObject.Create(9);
    writeln('my_int=', my_int.AsInteger);
    writeln('my_int.AsJson=', my_int.AsJson);

    my_array := TSuperObject.Create(stArray);
    my_array.I[''] := 1; // append
    my_array.I[''] := 2; // append
    my_array.I[''] := 3; // append
    my_array.I['4'] := 5;
    writeln('my_array=');
    with my_array.AsArray do
    for j := 0 to Length - 1 do
      if O[j] = nil then
        writeln(#9'[', j,']=', 'null') else
        writeln(#9'[', j,']=', O[j].AsJson);
    writeln('my_array.AsJson=', my_array.AsJson);

    my_object := TSuperObject.Create(stObject);
    my_object.I['abc'] := 12;
   // my_object.S['path.to.foo[5]'] := 'bar';
    my_object.B['bool0'] := false;
    my_object.B['bool1'] := true;
    my_object.S['baz'] := 'bang';
    my_object.S['baz'] := 'fark';
    my_object.AsObject.Delete('baz');
    my_object['arr'] := my_array;
    writeln('my_object=');
    if ObjectFindFirst(my_object, ite) then
    repeat
      writeln(#9,ite.key,': ', ite.val.AsJson);
    until not ObjectFindNext(ite);
    ObjectFindClose(ite);
    writeln('my_object.AsJson=', my_object.AsJson);

    new_obj := TSuperObject.Parse('"003"');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('/* hello */"foo"');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('// hello'#10'"foo"');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('"\u0041\u0042\u0043"');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('null');
    if new_obj = nil then
      writeln('new_obj.AsJson=', 'null');

    new_obj := TSuperObject.Parse('true');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('12');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('12.3');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('["\n"]');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('["\nabc\n"]');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('[null]');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('[]');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('["abc",null,"def",12]');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{}');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{ "foo": "bar" }');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{ "foo": "bar", "baz": null, "bool0": true }');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{ "foo": [null, "foo"] }');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{ "abc": 12, "foo": "bar", "bool0": false, "bool1": true, "arr": [ 1, 2, 3, null, 5 ] }');
    writeln('new_obj.AsJson=', new_obj.AsJson);

    new_obj := TSuperObject.Parse('{ foo }');
    if (new_obj = nil) then
      writeln('got error as expected');

    my_string := nil;
    my_int := nil;
    my_object := nil;
    my_array := nil;
    new_obj := nil;


    writeln(#10'press enter ...');
    readln;
  except
    on E: Exception do
      writeln(E.Message)
  end;
end.
person Wouter van Nifterick    schedule 06.01.2009

Я реализовал синтаксический анализатор в стиле pull на Java, который мне очень нравится использовать. Он анализирует строго соответствующий JSON с некоторыми послаблениями (в основном для моих конкретных целей). Он опубликован и подробно описан на моем веб-сайте. Также опубликован дополнительный метод, который иллюстрирует загрузку документа с помощью синтаксического анализатора, поэтому вы можете использовать его как для потоковой, так и для документной ориентации.

Я настоятельно рекомендую синтаксический анализ в стиле pull (у меня есть синтаксический анализатор XML, который также является pull).

person Lawrence Dol    schedule 16.01.2009
comment
Насколько я знаю, то, что вы называете синтаксическим анализатором в стиле pull, относится к семейству SAX, то есть запускает обратные вызовы для каждого элемента JSON. Это быстро, но, например, подход vtd-xml.sf.net более эффективен, поскольку он выиграл вам не нужно анализировать содержимое JSON/XML каждый раз, когда вы хотите получить доступ к узлу, поддерживая каталог в памяти или файле. - person Arnaud Bouchez; 05.06.2011
comment
@A.Bouchez: Нет, это не так. Это синтаксический анализатор в стиле event, но вместо вызова функции parse, которая отправляет события, вызывая прослушиватели, вы вызываете функцию nextEvent, которая извлекает следующее событие из потока. Разница незначительна, но я использовал оба и очень предпочитаю анализатор вытягивания. - person Lawrence Dol; 06.06.2011
comment
@A.Bouchez: Кстати, аббревиатура SAX расшифровывается как Simple API for XML, хотя многие люди связывают SAX с синтаксическим анализом на основе событий, поскольку это то, что он собой представляет, API, отправляющий события, хотя и специально для XML. - person Lawrence Dol; 06.06.2011
comment
@SM Спасибо за информацию. Насколько я знаю, то, что вы называете вытянутым событием, является обратным обратным вызовом. Как производится парсинг - дело вкуса. На самом деле, мой парсер Delphi JSON основан на вытягивании, если я следую вашей терминологии. Так что я согласен с вами, что я предпочитаю этот подход обратному вызову. Но мое замечание о том, что это будет заставлять анализировать весь контент JSON/XML каждый раз, когда вы обращаетесь к узлу, остается верным. Подход vtd-xml, создающий каталог, указывающий на данные JSON/XML, делает его однократным синтаксическим анализом. Во время извлечения мой парсер удалит содержимое на месте и создаст такой каталог. - person Arnaud Bouchez; 06.06.2011
comment
@A.Bouchez: То, что вы описываете, представляет собой форму загрузки документа, которую очень легко реализовать как слой поверх анализатора событий. Либо так, либо вы совершенно неправильно понимаете, как работает анализатор событий - он читает/анализирует только ту часть документа, которая требуется для получения следующего события, в конечном итоге обрабатывая весь документ по одному событию за раз. - person Lawrence Dol; 06.06.2011
comment
@SM 1. Прочитайте вопрос: речь шла о SAX и DOM, а не только о парсере. 2. Посмотрите мой ответ ниже и код моего синтаксического анализатора JSON, и вы увидите, что я понял, как работает синтаксический анализатор событий, поскольку я его написал. Пожалуйста, посмотрите на код, прежде чем делать предположения. Я прочитал твое. 3. Я подозреваю, что вы не усвоили подход vtd-xml, о котором я говорил, и реализованный на синтаксический анализатор в стиле pull, чтобы построить ИМХО самый быстрый парсер JSON в Delphi. Быстрее, чем ваша версия Java, во всех случаях. - person Arnaud Bouchez; 06.06.2011
comment
@A.Bouchez: Прошу прощения, если мой комментарий прозвучал снисходительно; это не было моим намерением, хотя, перечитывая комментарий, я понимаю, как это можно воспринять таким образом - я не собирался участвовать в соревновании по писсингу синтаксического анализатора. Я действительно понимаю подход VTD (фактически я использовал именно такой подход в других областях), и я думаю, что это довольно оригинальная альтернатива DOM, когда вам нужен структурированный документ в памяти. Я использовал JSON и XML, как правило, для загрузки и либо обработки их в процессе работы, либо преобразования на лету в совершенно другой формат, для чего идеально подходит анализатор на основе событий. - person Lawrence Dol; 06.06.2011

Когда речь идет о парсинге некоторого (текстового) контента, обычно предусматриваются два направления. В мире XML вам обычно приходится делать выбор между:

  • Парсер DOM, который создает в памяти древовидную структуру объектов, отображающих узлы XML;
  • Парсер SAX, который считывает XML-контент, затем вызывает предварительно определенные события для каждого элемента XML-контента.

Фактически, синтаксические анализаторы DOM используют внутренний синтаксический анализатор SAX для чтения содержимого XML. Таким образом, с накладными расходами на создание объектов и инициализацию их свойств синтаксические анализаторы DOM обычно в три-пять раз медленнее, чем SAX. Но синтаксические анализаторы DOM гораздо более эффективны для обработки данных: как только они отображаются в собственных объектах, код может мгновенно получить доступ к любому заданному узлу, тогда как доступ на основе SAX должен будет снова прочитать весь XML-контент.

Большинство синтаксических анализаторов JSON, доступных в Delphi, используют подход, подобный DOM. Например, модуль DBXJSON, включенный в Delphi 2010, или модуль SuperObject или DWS библиотеки создают экземпляр класса, отображающий каждый узел JSON.

В ORM клиент-сервер на основе JSON, подобном нашему, профилирование показывает, что много времени тратится на синтаксический анализ JSON как на стороне клиента, так и на стороне сервера (на сервере мы преобразуем содержимое JSON в SQL на лету). Поэтому мы постарались оптимизировать эту часть библиотеки.

Для достижения наилучшей скорости мы стараемся использовать смешанный подход:

  • Все необходимые преобразования (например, неэкранированный текст) выполняются в памяти, из буфера JSON и внутри него, чтобы избежать выделения памяти;
  • Синтаксический анализатор возвращает указатели на преобразованные элементы (так же, как библиотека vtd-xml).

Полученная скорость поразительна как для небольшого, так и для очень большого буфера содержимого JSON.

Для создания JSON мы также написали быстрый JSON. функции сериализации из содержимого любого объекта.

Подробности и источник кода см. в этой записи блога.

person Arnaud Bouchez    schedule 05.06.2011

Я согласен с Джеймсом; есть 3 разумных способа работы с Json: как с потоком событий/токенов; в виде дерева (например, XML DOM) или путем привязки к/из «собственных» объектов. Я знаком с пакетом Jackson (http://jackson.codehaus.org), и он также поддерживает эти 3 метода, похожие на то, как (я предполагаю) Json.NET.

person Community    schedule 15.01.2009

Ну, в JSON хорошо то, что он использует довольно простую грамматику, и написать парсер для него довольно просто. «забыв» все другие реализации, но используя Delphi, я бы начал с класса TParser в модуле классов для быстрого старта. Создайте отдельные методы для обработки каждого элемента (некоторые из них уже сделаны в TParser, поэтому я предложил начать с него).

Теперь, что делать с тем, что вы проанализировали. Вот где начинается самое интересное. Если вы имитируете интерфейсы и реализацию TXmlDocument, то преобразование в/из XML/JSON несколько тривиально.

person skamradt    schedule 05.01.2009

Jettison (http://jettison.codehaus.org/) – это популярный JSON-StAX (API потоковой передачи). для XML), написанный на Java.

Jettison — это набор API-интерфейсов Java (таких как STaX и DOM), которые читают и записывают JSON. Это позволяет почти прозрачно включать веб-службы на основе JSON в таких платформах служб, как CXF, или в средах сериализации XML, таких как XStream.

person mjn    schedule 06.06.2011