Search code examples
javajerseyjax-rsgenson

What is a ContextResolver and Provider in Jersey?


What is a ContextResolver in Jersey and what is a Provider? What is the difference between the two? I am using Genson with Jersey. Genson is auto-registered when Jersey finds the Genson JAR on the classpath. The Genson JAR's WEB-INF/services directory contains a file called "org.glassfish.jersey.internal.spi.AutoDiscoverable".

Following that AutoDiscoverable path, by default Genson/Jersey auto-registers a the following class:

@Provider
@Consumes({MediaType.APPLICATION_JSON, "text/json", "application/*+json"})
@Produces({MediaType.APPLICATION_JSON, "text/json", "application/*+json"})
public class GensonJsonConverter implements MessageBodyReader<Object>, MessageBodyWriter<Object> {

     private final ContextResolver<GensonJaxRSFeature> _gensonResolver;

Here's where more confusion comes into play: looking at the Genson documentation it recommends to create a custom provider like so:

    @Provider
    public class GensonProvider implements ContextResolver<Genson> {
    private final Genson genson = new GensonBuilder().setSkipNull(true).create();
    }

However that provider implements a ContextResolver not a MessageBodyReader/Writer like the internal Genson one does. What's the difference? Also, that provider does not do the same thing as the default auto-registered one does! In particular, it ignores JAXB tags like @XmlTransient! Digging into the Genson source code for GensonJaxRSFeature, I see that the Genson object is created like so:

private static final Genson _defaultGenson = new GensonBuilder()
      .withBundle(new JAXBBundle())
      .useConstructorWithArguments(true)
      .create();

From that and from the Genson documentation I can see that the "JAXBBundle" is probably what causes Genson to pay attention to the JAXB annotations.

The main issue:

I want to use the default Genson JSON provider that is auto-registered with Jersey, but I want to set a few custom properties on it. As I said, when I register my custom provider, it doesn't use the default Genson one!


Update:

This is what I'm doing right now and it works. However, the solution below by @eugen is the Genson recommended solution.

@Provider
public class GensonProvider implements ContextResolver<GensonJaxRSFeature> {
    private final GensonJaxRSFeature _gensonResolver = new GensonJaxRSFeature();

    private static final Genson _defaultGenson = new GensonBuilder()
              .withBundle(new JAXBBundle())
              .useConstructorWithArguments(true)
              .setSkipNull(true)
              .create();

   @Override
   public GensonJaxRSFeature getContext(Class<?> type) {
        return _gensonResolver.use(_defaultGenson);
   }
}

Solution

  • Like always in our world for the same problem there are multiple solutions. Jersey seems to encourage the use ResourceConfig instead of defining custom providers. So this is how you can achieve it using a resource config (from jersey docs here and Genson documentation here).

    public class MyApplication extends ResourceConfig {
        public MyApplication() {
          Genson genson = new GensonBuilder()
                  .withBundle(new JAXBBundle())
                  .useConstructorWithArguments(true)
                  .setSkipNull(true)
                  .create();
    
          register(new GensonJaxRSFeature().use(genson));
        }
    }
    

    But of course the way you do it with a provider is fine too.