Search code examples
spring-bootjava-8jacksonfasterxml

creating multiple beans for ObjectMapper


I want to create multiple beans for ObjectMapper, mostly differentiating in date format.

I have written the following code -

@Configuration
public class ObjectMapperConfig {

    @Primary
    @Bean
    public ObjectMapper createDefaultObjectMapper() {
        return new ObjectMapper();
    }

    @Bean(name="AOMapper")
    public ObjectMapper createFacebookObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ"));
        return objectMapper;
    }

    @Bean(name="BOMapper")
    public ObjectMapper createTwitterObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy"));
        return objectMapper;
    }
}

While I am able to use AOMapper and BOMapper using @Qualifier annotation, other third party packages which use just ObjectMapper without the Qualifier annotation are unable to get default ObjectMapper.

Is there a way out?

Update: Following is the exception I get

[com.myorg.service.xyz.client.XyzClient]: Factory method 'getUnstarted' threw exception; nested exception is java.lang.RuntimeException: java.io.UncheckedIOException: Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): no String-argument constructor/factory method to deserialize from String value ('2018-03-19T22:56:33.339Z')
 at [Source: (ByteArrayInputStream); line: 1, column: 161] (through reference chain: com.myorg.service.xyz.client.WatchResult["updates"]->java.util.ArrayList[0]->com.myorg.service.xyz.client.Announcement["announceTime"]) (code 200)

What I understood from above is that somehow, the XyzClient is not getting the correct default ObjectMapper and therefore unable to deserialize the date format


Solution

  • The problem is that your ObjectMapper don't know how to deserialize a Instant class, you need to register the jackson module that provides those custom serializers/deserializers. This module is named jackson-datatype-jsr310 and you can import it (when using maven) with this:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.9.4</version>
    </dependency>
    

    Then, when you create your object mapper you have to register the JavaTimeModule module.

    objectMapper.registerModule(new JavaTimeModule());
    

    So your method should be similar to:

    @Bean
    @Primary
    public ObjectMapper createDefaultObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }