Search code examples
jacksonjax-rsjersey-2.0jackson-modules

Register JodaModule in Jax-RS Application


I'm writing a Jax-RS application using Jersey, and Jackson2 under the hood to facilitate JSON i/o. The service itself works fine, but I'd like to improve it by having the Jackson mapper automagically serialize/deserialize date and date-times to JodaTime objects.

I'm following the documentation here and have added the relevant jars, but I'm lost on this instruction:

Registering module

To use Joda datatypes with Jackson, you will first need to register the module first (same as with all Jackson datatype modules):

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());

I've tried to do this in the custom class that extends jax.ws.rs.core.Application, but I'm not at all confident in that solution. I'm currently getting this error:

Can not instantiate value of type [simple type, class org.joda.time.DateTime] from String value ('2014-10-22'); no single-String constructor/factory method
 at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@3471b6d5; line: 7, column: 25]

Other than the general impression that this module registration needs to happen at application (servlet?) startup, I have no idea what to do with this information. Do I need to annotate a custom class with something in particular to have it picked up ? Should I be extending some class ?

The examples I find on StackOverflow usually stick it in main() and call the mapper directly, but I'm relying on Jackson Databinding so the examples aren't relevant. Any direction is appreciated.


Solution

  • You'll basically want to create/configure/return the ObjectMapper in a ContextResolver. Something like

    @Provider
    public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
    
        final ObjectMapper mapper = new ObjectMapper();
    
        public ObjectMapperContextResolver() {
            mapper.registerModule(new JodaModule());
        }
    
        @Override
        public ObjectMapper getContext(Class<?> type) {
            return mapper;
        }  
    }
    

    If you are using package scanning to discover your resources, then the @Provider annotation should allow this class to be discovered and registered also.

    Basically what happens, is the the MessageBodyReader and MessageBodyWriter provided by Jackson, used for unmarshalling and marshalling, respectively, will call the getContext method in the ContextResolver, to determine the ObjectMapper to use. The reader/writer will pass in the class (in a reader it will be the type expected in a method param, in a writer it will be the type returned as-a/in-a response), meaning we are allowed to use differently configured ObjectMapper for different classes, as seen here. In the above solution, it is used for all classes.