the problem
Let's say you have some classes coming from another library that you need to serialize into JSON using Jackson. You can't manipulate the source code of these classes one reason or another, and you have a problem:
These classes don't serialize correctly
There are a number of reasons this can happen, but in this post we're going to focus on two examples: two classes that have a recursive relationship to one another, and a class that doesn't conform to the bean spec.
dealing with recursive relationships
Let's say we have two classes, User
and Thing
, as shown below. User
has a one to many relationship with Thing
, and Thing
has a many to one relationship back to its parent, User
:
Given these classes, let's say in a unit test we create users using the following code, establishing the recursive relationship:
Now that we can create a user, let's try serializing:
If you run that method, your test will fail, and you'll see a nice CPU spike on your computer because this just happened:
fixing recursive relationship issues with mixins
As it turns out, Jackson has an awesome feature called mixins that can address this type of problem (remember, we're assuming User
and Thing
are not modifiable).
Mixins allow you to create another class that has additional Jackson annotations for special serialization handling, and Jackson will allow you to map that class to the class you want the annotations to apply to. Let's create a mixin for Thing
that specifies a @JsonFilter
:
Now you might be thinking, "What's that 'thing filter' string referencing?" We have to add this mixin to the object mapper, binding it to the Thing
class, and then we have to create a filter called "thing filter" that exludes Thing
's field of user
, as shown in the test below:
If you run this test, you'll see that it passes.
dealing with a class that doesn't conform to the bean spec
Let's say we have two other classes, Widget
and WidgetName
. For some reason, the person who wrote WidgetName
decided to not conform to the bean spec, meaning when we serialize an instance of Widget
, we can't see data in WidgetName
:
Let's say we're creating widgets using the code below:
If we try to create a widget like this and serialize it, we won't see the name. Here's a test that will serialize:
And here's the output:
fixing classes that don't conform to the bean spec with modules and customer serializers
We can address this problem pretty easily using a Jackson module that provides a custom serializer for WidgetName
. The serializer can be seen below, and it uses the JsonGenerator
instance to write the value from the WidgetName
argument:
We now need to wire this up in order to use it. Below is a test that creates a SimpleModule
instance, wires up our custom serializer to it, and registers the module within our ObjectMapper
instance:
If you run this test, you can see the correct output for the serialization:
conclusion and resources
All the resources used in this example can be found on Github at https://github.com/theotherian/jackson-mixins-and-modules. If you end up having to serialize data that's outside of your control and is causing you problems, you should be able to get a lot of mileage out of these two solutions.