Я думаю о реализации парсера JSON для Delphi. Что должен делать хороший парсер JSON? Любые идеи для требований? Я считаю, что он должен по крайней мере выводить и обрабатывать JSON... Глядя на синтаксические анализаторы XML, должен ли он быть больше похож на DOM или SAX?
Требования к синтаксическому анализатору JSON
Ответы (8)
Я думаю, что Json.NET отлично справляется со своей задачей.
- JsonReader и JsonWriter для низкоуровневой обработки и вывода JSON.
- JsonSerializer для преобразования объектов в JSON и обратно
- Классы JObject, JArray и JValue для работы с JSON в объектной модели
Обратите внимание, что я могу быть немного предвзятым, так как я написал это :)
Хорошим основанием будет вся функциональность, предоставляемая следующими тремя (взято с JSON.org): uJson, инструментарий JSON и lkjson.
Я использовал набор инструментов 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.
Я реализовал синтаксический анализатор в стиле pull на Java, который мне очень нравится использовать. Он анализирует строго соответствующий JSON с некоторыми послаблениями (в основном для моих конкретных целей). Он опубликован и подробно описан на моем веб-сайте. Также опубликован дополнительный метод, который иллюстрирует загрузку документа с помощью синтаксического анализатора, поэтому вы можете использовать его как для потоковой, так и для документной ориентации.
Я настоятельно рекомендую синтаксический анализ в стиле pull (у меня есть синтаксический анализатор XML, который также является pull).
Когда речь идет о парсинге некоторого (текстового) контента, обычно предусматриваются два направления. В мире 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. функции сериализации из содержимого любого объекта.
Подробности и источник кода см. в этой записи блога.
Я согласен с Джеймсом; есть 3 разумных способа работы с Json: как с потоком событий/токенов; в виде дерева (например, XML DOM) или путем привязки к/из «собственных» объектов. Я знаком с пакетом Jackson (http://jackson.codehaus.org), и он также поддерживает эти 3 метода, похожие на то, как (я предполагаю) Json.NET.
Ну, в JSON хорошо то, что он использует довольно простую грамматику, и написать парсер для него довольно просто. «забыв» все другие реализации, но используя Delphi, я бы начал с класса TParser в модуле классов для быстрого старта. Создайте отдельные методы для обработки каждого элемента (некоторые из них уже сделаны в TParser, поэтому я предложил начать с него).
Теперь, что делать с тем, что вы проанализировали. Вот где начинается самое интересное. Если вы имитируете интерфейсы и реализацию TXmlDocument, то преобразование в/из XML/JSON несколько тривиально.
Jettison (http://jettison.codehaus.org/) – это популярный JSON-StAX (API потоковой передачи). для XML), написанный на Java.
Jettison — это набор API-интерфейсов Java (таких как STaX и DOM), которые читают и записывают JSON. Это позволяет почти прозрачно включать веб-службы на основе JSON в таких платформах служб, как CXF, или в средах сериализации XML, таких как XStream.