views:

1209

answers:

7

I'm thinking about implementing JSON parser for Delphi. What should a good JSON parser do? Any ideas for requirements? I believe it should at least output and process JSON... Looking at XML parsers, should it be more DOM or SAX-like?

+2  A: 

A good floor would be all the functionality provided by the following 3 (taken from JSON.org): uJson, JSON Toolkit, and lkjson.

Hank Gay
A: 

OK. These are all three JSON parsers listed on JSON.org. I'm not happy with none of them. Let's forget about Delphi for a sec. I'm looking for general requirements for a JSON parser.

pglowack
They all parse JSON. Without some criteria for why they aren't satisfactory, it's hard to create a list that will satisfy you.
Hank Gay
That's like chat;-) Give me a sec and I'll try to be more verbose:-)
pglowack
When you've decided what you want, update the _question_. Don't post another answer.
Rob Kennedy
OK. I need more seconds;-)
pglowack
A: 

Well, the nice thing about JSON is that it uses a fairly simple grammar and writing a parser for it is fairly simple. "forgetting" all of the other implementations, but using Delphi I would start with the TParser class in the classes unit for a jumpstart. Create separate methods to handle each element (some are already done in TParser, which is why I suggested starting from there).

Now, what to do with what you have parsed. There is where the fun comes in. If you mimic the interfaces and implementation of the TXmlDocument, then conversion to/from XML/JSON is somewhat trivial.

skamradt
+1  A: 

I've used the JSON toolkit in several projects, with great success. The only thing I've modified at some point was the way it formats resulting JSON, but it's a matter of personal taste.

It's free, fairly clean, and easy to use. No need to install packages; just have a .pas file somewhere in your path. Just check test_usage.dpr below for some simple examples on how to use it. It doesn't get much easier than that.

I wouldn't waste my time trying to implement yet another JSON parser, unless you want to do it for educational purposes, in which case you should carefully study existing implementations anyway.

JSON Toolkit home: 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.
Wouter van Nifterick
+3  A: 

I think Json.NET does a pretty bang up job.

  • JsonReader and JsonWriter for low level work of parsing and outputting JSON
  • JsonSerializer for converting objects to and from JSON
  • JObject, JArray and JValue classes for working with JSON in an object model

Note I may be a bit bias since I wrote it :)

James Newton-King
Absolutely -- separation between streaming/incremental parser/generator, and higher-level full in-memory constructs is a must in my opinion.
StaxMan
A: 

I agree with James; there are 3 sensible ways to work with Json: as a stream of events/tokens; as a tree (like XML DOM), or by binding to/from "native" objects. The package I am familiar with is Jackson (http://jackson.codehaus.org), and it also supports these 3 methods, similar to how (I assume) Json.NET does.

+1  A: 

I have implemented a pull-style parser in Java, which I find very nice to use. It parses strictly conforming JSON, with some relaxations accepted (primarily for my specific purposes). It is published and detailed on my website. Also published is an additional method which illustrates a document load using the parser - so you can use it either stream-oriented or document oriented.

I highly recommend pull style parsng (I have an XML parser which is pull, also).

Software Monkey