Finally, in my Semantic Web developer career, I got to that point when I had to do some work with JSON in Java. And as it goes, typically one needs to read some JSON, from an API, and make it available in a POJO (deserialize) and vice-versa (serialize).
The only requirement I had, was to avoid using annotations of any kind, such as
@JsonDeserialize. Let’s take as an example a JSON object describing a person:
Please notice the
@name property and that the
degree property is a nested JSON object which can contain a different number of sub-properties, not known in advance.
First, we deserialize the above JSON into a Person POJO, then some data gets changed, and then it gets serialized back to a JSON object.
The POJO of the above example JSON is called Person.java and looks like this:
The focus is now on the serializing of this POJO.
Since I could not use annotations
@JsonDeserialize), I had to write a custom serializer or adapter (depending on which JSON library is used). This is where the peculiarities started.
Let’s start with Jackson.
I pulled in Jackson version 2.13.4 from Maven and started with a Custom Serializer. The custom serializer needs to override the
serialize method of the extended
on> class. In the
serialize method is where the customization happens:
Above, I am attempting to write the
degree property, which in the POJO is of type
Map<String, Object>, out as a string. This ends up being serialized as a JSON as follows:
did solve the
@name property but the
degree property is quite wrong! It is a String.
So how can this be improved with Jackson?
The solution is to use
writePOJOField(). The exact and correct code looks like follows:
Awesome! Solved it for Jackson. Find the full code on GitHub.
Let’s see the solution also using JSON-B.
In the case of JSON-B, I needed to write an adapter to deal with the
@name property. As goes, the adapter needs to override the
adaptToJson (serialize) and
adaptFromJson (deserialize) methods from the
JsonbAdapter<Person, JsonObject> interface. So, the first go at it looks like the following (and I only focus on the
adaptToJson method here):
If we take a look at the serialized JSON, we have again the same problem as with Jackson. The
@name property is ok but the
degree property is wrong again! The generated JSON looks like follows:
So how can this be done any better?
While in Jackson we had the brilliant
writePOJOField method, JSON-B does not have such a method. My idea was for the
adaptToJson method to use a helper method called
addRightJsonType. So the correct
And the helper method
addRightJsonType is a recursive method, that tries to catch all possible types of the entry value and deal with it accordingly:
Find the complete code on GitHub, which includes also the code for
When I set out to serialize JSON in Java, little did I know that this task will come with some peculiarities. Writing a Jackson serializer or a JSON-B adapter means one needs to specify in detail how each property is to be handled. There are no shortcuts like using .toString() on a Map<String, Object>
degree property. For some reason, I initially thought that by simply omitting it completely, it would still magically know how to deal with it 😅.
I also see how Jackson, by having a dedicated writePOJOField method, can be considered the most mature Java JSON parsing library. I also say this, because while searching online for solutions, other libraries also had the same problems as Yasson: no dedicated method to do this simple task.
Remark: for code simplicity, some if statements were omitted in the above code. See the full code on GitHub.