Native JSON handling in Java with EasyGson

Can you handle JSON natively in Java? The very short answer: no. It is possible to get a near-native JSON handling experience, for example with EasyGson. There is a price to pay, though. You will have to forgo standard Java best practices and accept that the JSON itself can be the master data source in your domain.




Unlike languages such as JavaScript, Ruby and Python, Java is not capable of handling JSON in a native way. Let us suppose you have the following data structure:


"somedata" : [
{ "someproperty" : "sometext 1" },
{ "someproperty" : "sometext 2" },
{ "someproperty" : "sometext 3" }
]


In JavaScript you would be able to execute this:


somedata[0].someproperty = "sometext";


The conventional way to treat JSON in Java is to convert the data to a series of domain classes. This is a perfectly fine way to do things, especially if the storage for the data is not a JSON storage. For example, a relational database. In this case, the data will have to be converted from relational to Java classes anyway, mostly with the help of ORM tooling, a well-trodden path. It is a logical step to introduce a full conversion from JSON to Java classes and vice versa. Both Jackson and Gson provide excellent tooling to help you with this conversion.


01-traditional-conversions

With the rise of NoSQL databases in general and JSON-oriented document databases in particular, such as MongoDB, the advantages of a full JSON-Java conversion start to erode quickly. The only beneficiary of this conversion would be the Java layer. This could still be worth it, relative to the amount of interaction the Java layer demands to have with the data: the more interactions, the better the fit.


02-nosql-conversions

Besides the solutions above, you can also go JSON-all-the-way, like this:


03-json-all-the-way

What is immediately obvious, is the lack of tools for conversion to other data formats. In this model, these tools are no longer required. Technically, this is not entirely true, since conversions from plain text to abstract syntax trees still take place. These require no custom mapping, however, which is where the costs of conversions between layers come from. For the sake of this argument, these automatic conversions are not counted.



So, you have decided you want to go JSON-all-the-way in Java. Next step would be picking the right tools for the job. If you are used to languages that natively support JSON handling, you will be disappointed when you find out that Java does not offer this.



Gson goes a long way towards treating the JSON object as the definitive data source. There are a number of architecture choices in Gson that get in the way of choosing it to help us out:



  • Different node types have their own class; Gson has the abstract JsonElement, which has three relevant subclasses, JsonArray, JsonObject and JsonPrimitive. Whereas the division itself is logical, it gets in the way when you are handling the data. For example, if you want to loop over the elements of a JsonArray, you will first have to cast the object to this class. In doing so, it exposes it internals, "protects" the developer from calling the wrong functionality and generally moves away from the JavaScript way of doing things.

  • Aimed at reading or constructing, not changing; whereas JsonObject is freely changeable, JsonArray only allows creation of rows within its array. If you intend to manipulate arrays, this is not what you are looking for.

  • No fluent API; if you call JSON nodes in JavaScript, you can chain your call, allowing you to reach data with a single path expression. Gson forces you to place most calls on separate lines. API fluency should be a core attribute of JSON handling in any language



EasyGson is a wrapper component for Gson. Gson does most things well, expect for the use case described above. EasyGson uses the good parts of Gson and adds its own sauce to deliver what Gson does not.



  • One JSON class; JsonEntity undoes the division into various JSON node types and offers a single interface. If you call the wrong method on a node, an exception will be thrown. You will not be bored with details on the internals, as you have just the one interface to work with.

  • Arrays can be changed; you can remove array elements or overwrite them with new ones. Note that this will trigger an array reconstruction

  • API fluency; if you create elements in a JSON tree, it will return the newly created element (if it is an object or array). This allows large, chained method calls to build complete trees.



Some examples of the things you can do with EasyGson:



    JsonEntity array = JsonEntity.emptyArray()
.create("line 1")
.create("line 2")
.create("line 3");

for (JsonEntity line : array) { System.out.println(line.asString()); }


A JsonEntity is Iterable, so this will print out all the array elements.



    array.remove(1);

for (JsonEntity line : array) { System.out.println(line.asString()); }


"line 2" will no longer show in the output.



    array.create(1, JsonEntity.emptyObject()
.createObject("coordinates")
.create("x", "11")
.create("y", "38")
.parent()
);

System.out.println(array.get(1).get("coordinates").asInt("x"));
System.out.println(array.get(1).get("coordinates").asInt("y"));


Noteworthy here is the method call to parent(). Since JsonEntity has a fluent API, it must be told explicitly if you want to move one level up. In this case, the fluent API returns the newly created Object called "coordinates", whereas you want to store the root object. Hence the use of parent().



If you go to work with EasyGson and have issues, please be sure to report the problem on the project page. We hope you enjoy your JSON-all-the-way experience.