Jen-Ming Chung

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.

1
2
3
4
5
6
7
8
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.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "_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 for ObjectMapper instead of setting @JsonSerialize across POJO classes.

1
2
3
4
5
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.

1
2
3
4
5
6
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.7</version>
    <scope>provided</scope>
</dependency>
1
2
3
4
5
6
7
@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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 for ObjectMapper and register the module in ObjectMapper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.

1
2
3
4
5
6
7
8
@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.

1
2
3
4
5
6
{
  "_id": "53a06d5630049df4cc06a38a",
  "username": "John Smith",
  "password": "am9obiBzbWl0aCBwYXNzd29yZA",
  "createdAt": 1403022678341
}

References

Comments