A Fast and Minimal JSON Parser for Java

In the RAP project, reading and writing JSON are critical operations, since the server processes and creates JSON messages for a large number of clients at a high rate. For this reason, we need something fast for this job. When we switched to JSON, we included the org.json parser, which is reasonably small but not famous for its performance.

There are many better JSON libraries out there, but most do much more than we need. We really only need a bare-bones parser that can read JSON into a simple Java representation and generate JSON from Java. As we like to keep the core library self-contained, we don’t want a dependency to an external JSON library.

One winter Sunday, I started to write a JSON parser just for the fun of it, and was quickly surprised how simple it is to parse JSON.

[ Looking for more useful tools? See our Eclipse Tools page. | Need expert advice for your project? Our Developer Support is here to resolve your questions. ]

Why is JSON parsing so easy? That’s because the first character of every token uniquely defines its type ('[' for an array, '"' for a string, 't' or 'f' for a boolean, and so forth). There’s no backtracking involved. It went so well that I decided to continue and create a JSON parser tailored to our needs. Which are:

  • Fast – we read and create so much JSON that the parser directly affects the server performance
  • Lightweight – it should deal with memory sparingly as we deal with lots of messages
  • Minimal – the less code the better, as we have to maintain it
  • Simple to use – we’ll expose the API for custom component developers, so it should be simple and clear
  • No dependencies – only Java 5

The result is called minimal-json and it’s already included in RAP. It is fast, lightweight, consists only of 10 classes and I hope it’s simple to use:

Usage

You can read a JSON object or array from a Reader or from a String:

JsonObject jsonObject = JsonObject.readFrom( reader );
JsonArray jsonArray = JsonArray.readFrom( string );

Once you have a JsonObject, you can access its contents using the get() method:

String name = jsonObject.get( "name" ).asString();
int age = jsonObject.get( "age" ).asInt(); // asLong(), asDouble(), ...

The elements of a JSON array can be accessed in a similar way:

String name = jsonArray.get( 0 ).asString();
int age = jsonArray.get( 1 ).asInt(); // asLong(), asDouble(), ...

As you can see, the get() method always returns an instance of JsonValue, which can then be transformed to the target type using asString(), asInt(), asDouble(), etc. There’s no automatic conversion to Java types, no instanceof needed. If you’re not sure about the type of a value you can check it using isString(), isNumber(), etc.

Nested arrays and objects can be accessed using asArray() and asObject():

JsonArray nestedArray = jsonObject.get( "items" ).asArray();

You can also iterate over the elements of an JsonArray and the names of a JsonObject, e.g.:

for( String name : jsonObject.names() ) {
  JsonValue value = jsonObject.get( name );
  ...
}

Writing JSON

A JsonObject or JsonArray can output JSON to a Writer or as a string using the toString() method. The JSON is currently not pretty-printed, formatting support might be added later.

jsonObject.writeTo( writer );
String json = jsonArray.toString();

To create a JsonObject or a JsonArray, use the add() methods that exist for the relevant types. These methods return the object instance to allow method chaining:

jsonObject = new JsonObject().add( "name", "John" ).add( "age", 23 );
jsonArray = new JsonArray().add( "John" ).add( 23 );

You may have noticed that also the object has an add() method instead of a put() or set(). That’s because the JsonObject stores and writes its members in the order they are added. It allows you to define the output order, it even allows you to add the same key twice, which is discouraged but not forbidden by the JSON RFC.

To replace an element in an array of object, you first have to remove() the old value and then add() the new one. A replace() method may be added later. However, JsonArray and JsonObject are designed to be containers for reading and writing JSON, not general purpose data structures.

Performance

I’ve compared the time required to read and write a typical RAP message with other popular parser implementations, namely org.json (20091211), Gson (2.2.2), Jackson (1.9.12), and JSON.simple (1.1). Disclaimer: This benchmark is restricted to our use case and my limited knowledge on the other libraries. It may be unfair as it ignores other use cases and perhaps better ways to use these libraries. However, I think these results show that minimal-json can take comparison with state-of-the-art parsers.


Overall performance
I also ran a number of micro-benchmarks using Google caliper while optimizing the implementation. One interesting detail was the choice of the data structure for the JsonObject. Since JSON objects are key-value maps, a HashMap seems like an obvious choice. However, after some experiments, I ended up with two separate ArrayLists for names and values.

Of course, looking up a key in a list requires a linear search while a hash lookup is much quicker. However, creating a HashMap and adding elements has a considerable overhead. It turns out that this overhead squashes the benefits of a HashMap for very small numbers of items (< 10). Since JSON messages in RAP typically contain many objects with only a few items, a HashMap would even impair the overall performance. Moreover, the two ArrayLists require less than a third of the footprint of a HashMap.


Lookup performance
I was able to improve the lookup performance for small item counts by adding a very small hash structure to the names list. This structure consists of a 32-elements byte array. It does not handle collisions but resorts to indexOf() in case of a miss. This version (shown in the middle of the chart above) seems to be a good compromise between HashMap and plain ArrayList. Optimizations for bigger JSON objects are possible, but not needed at the moment.

How to Use it

If you’re looking for a bare-bones JSON parser with zero dependencies, you are welcome to use minimal-json. It goes without saying that it’s developed test-driven and complies with the RFC. The code lives at github and is EPL-licensed. It is also included in RAP.

I didn’t setup a build. If you would like to use the code, I suggest that you simply copy these 10 Java files to your project.

Let me know if you find this useful, fork it on github, and feel free to open an issue if you find a problem.

12 Comments

Sorry, the comment form is closed at this time.