How to Customise the Jackson JSON ObjectMapper in Java EE Enterprise Application
Assume we have a naive User POJO class with a BSON Type ObjectId field, i.e., id.
public class User {
private ObjectId id;
private String username;
private String password;
private Date createdAt;
public getters/setters;
...
We can expect the following outputs from our REST services which is not a String
with 24 hex characters.
{
"_id": {
"new": false,
"machine": 805608948,
"timeSecond": 1403022678,
"inc": -871980150,
"time": 1403022678000
},
"username": "John Smith",
"password": "am9obiBzbWl0aCBwYXNzd29yZA",
"createdAt": 1403022678341
}
As we know, it can be solved by using the Jackson annotation for configuring Serializer class to serialize the associated value. If the project/application has identified a consistent output format in REST responses, we can leverage ContextResolver
public class User {
@JsonSerialize(using=ObjectIdJsonSerializer.class)
private ObjectId id;
...
}
ContextResolver<T> for ObjectMapper
In order to use Jackson as our JSON provider instead of MOXy in Glassfish 4 we need to set the dependency in pom.xml
and register JacksonFeature in ApplicationConfig
.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.7</version>
<scope>provided</scope>
</dependency>
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
resources.add(JacksonFeature.class);
addRestResourceClasses(resources);
return resources;
}
Jackson provides the ability to register bundles of extended functionality as modules. Here we build a ObjectIdSerializerModule which extends SimpleModule
to convert BSON ObjectId into String.
public class ObjectIdSerializerModule extends SimpleModule {
public ObjectIdSerializerModule() {
super("ObjectIdSerializerModule", new Version(0, 1, 0, "alpha"));
this.addSerializer(ObjectId.class, new ObjectIdSerializer());
}
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
@Override
public void serialize(ObjectId value, JsonGenerator jgen
, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeString(value.toString());
}
}
}
Next we need to implement a ContextResolver
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper defaultObjectMapper;
public JacksonObjectMapperProvider() {
defaultObjectMapper = createDefaultMapper();
}
@Override
public ObjectMapper getContext(Class<?> type) {
return defaultObjectMapper;
}
private static ObjectMapper createDefaultMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ObjectIdSerializerModule());
return mapper;
}
}
Register
Finally, the ContextResolver must be either programmatically registered in a JAX-RS runtime or must be annotated with @Provider annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.
Finally, the ContextResolver must be annotated with @Provider
annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.
@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
...
}
or must be registered in ApplicationConfig
.
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
resources.add(JacksonFeature.class);
resources.add(JacksonObjectMapperProvider.class);
addRestResourceClasses(resources);
return resources;
}
Access the previous REST again you will get the following JSON results.
{
"_id": "53a06d5630049df4cc06a38a",
"username": "John Smith",
"password": "am9obiBzbWl0aCBwYXNzd29yZA",
"createdAt": 1403022678341
}
References
- Support for Common Media Type Representations, a chapter in Jersey 2.9 User Guide.
- Feature: Jackson Modules, a chapter in FasterXML Wiki.
- How Do I Write a Jackson JSON Serializer & Deserializer?, by Steve Hill.
- Jackson Mapper Serialize/Deserialize ObjectId, by user1745356.