Custom ObjectMapper with Jersey 2.2 and Jackson 2.1
Asked Answered
S

8

53

I am struggling with a REST application with Grizzly, Jersey and Jackson, because Jersey ignores my custom ObjectMapper.

POM dependencies:

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-servlet</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>2.1.4</version>
    </dependency>
</dependencies>

Resulting versions are: Grizzly 2.3.3, Jackson 2.1.4 and Jersey 2.2.

Main class (I want explicit registration of Jersey components):

public class Main {
    public static void main(String[] args) {
        try {
            ResourceConfig rc = new ResourceConfig();
            rc.register(ExampleResource.class);
            rc.register(ObjectMapperResolver.class);

            HttpHandler handler = ContainerFactory.createContainer(
                    GrizzlyHttpContainer.class, rc);

            URI uri = new URI("http://0.0.0.0:8080/");

            HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);

            ServerConfiguration config = server.getServerConfiguration();
            config.addHttpHandler(handler, "/");

            server.start();
            System.in.read();

        } catch (ProcessingException | URISyntaxException | IOException e) {
            throw new Error("Unable to create HTTP server.", e);
        }
    }
}

ContextResolver for ObjectMapper:

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperResolver() {
        System.out.println("new ObjectMapperResolver()");
        mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        System.out.println("ObjectMapperResolver.getContext(...)");
        return mapper;
    }

}

Neither ObjectMapperResolver constructor nor getContext get called. What am I missing? I would prefer to use Jersey 2.2 and Jackson 2.1, because it is a dependency for another lib.

A full example can be found on GitHub: https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow

Stuyvesant answered 18/9, 2013 at 12:58 Comment(6)
What are you struggling with, exactly?Gramercy
Jersey ignores my ObjectMapperResolver. I registered it with rc.register(ObjectMapperResolver.class);, but that doesn't work.Stuyvesant
Do your constructor and getContext methods get called? Why do you have this anotation: @Produces(MediaType.APPLICATION_JSON)? I would remove it.Folliculin
Neither the constructor nor getContext get called. There is no reason for @Produces ...Stuyvesant
We had a similar problem, but with Jersey 1.13. For us, we needed to use org.codehaus.jackson.map.ObjectMapper and not com.fasterxml.jackson.databind.ObjectMapper. We figured this out from looking at the sample project in chapter 5 of jersey.java.net/nonav/documentation/1.13/user-guide.htmlContemporary
I solved my problem with this instructions... REST Web Services with Jackson, Jersey and Payara Micro ... Level IILightman
S
36

I found a solution. I had to instantiate the Jackson Provider by myself and set my custom ObjectMapper. A working example can be found on GitHub: https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow-answer

I deleted my ObjectMapperResolver and modified my main method:

public class Main {
    public static void main(String[] args) {
        try {
            // create custom ObjectMapper
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);

            // create JsonProvider to provide custom ObjectMapper
            JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
            provider.setMapper(mapper);

            // configure REST service
            ResourceConfig rc = new ResourceConfig();
            rc.register(ExampleResource.class);
            rc.register(provider);

            // create Grizzly instance and add handler
            HttpHandler handler = ContainerFactory.createContainer(
                    GrizzlyHttpContainer.class, rc);
            URI uri = new URI("http://0.0.0.0:8080/");
            HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
            ServerConfiguration config = server.getServerConfiguration();
            config.addHttpHandler(handler, "/");

            // start
            server.start();
            System.in.read();

        } catch (ProcessingException | URISyntaxException | IOException e) {
            throw new Error("Unable to create HTTP server.", e);
        }
    }
}
Stuyvesant answered 20/9, 2013 at 13:22 Comment(6)
This is very helpful, but it still has a problem: Jersey auto-discovers the Jackson provider so it creates a (presumably unconfigured) provider, and then this code explicitly registers a (properly configured) provider, so Jersey ends up with two providers. I've illustrated this at (github.com/svenwltr/example-grizzly-jersey-jackson/issues/1). I don't know how to fix this. Any ideas?Dreddy
I believe you can turn auto-discovery off but then you lose it for everything.Schramke
The given example is working for me. Also, try user915662s solution.Stuyvesant
This worked for me when I was trying to run some test cases with protos. NicePlica
you can disable autodiscovery with resourceConfig.addProperties(Collections.singletonMap(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true))Kolinsky
I did not need to explicitly reference the ObjectMapperContext. Just make sure you have it under a package, say com.yourcompany.a.b.c and then ensure you have this instruction in your ResourceConfig (or main): packages("com.yourcompany.a.b.c");Forayer
K
49

The following solution applies to the following stack (as in... this is the setup I've used to test it)

Jersey 2.12, Jackson 2.4.x

I'm adding my message w/ the solution I've come up with on this post since it was quite relevant for the many Google searches I've put in today... It is a cumbersome solution to what I believe to be an even more cumbersome problem.

1. Make sure your maven configuration CONTAINS the jackson-jaxrs-json-provider dependency:

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.1</version>
</dependency>

2. Make sure your maven configuration DOESN'T CONTAIN the jersey-media-json-jackson dependency:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>

3. Create a @Provider component extending com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider like so:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class CustomJsonProvider extends JacksonJaxbJsonProvider {

    private static ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
     }

    public CustomJsonProvider() {
        super();
        setMapper(mapper);
    }
}

As you can observe this is also where we define the custom instance of com.fasterxml.jackson.databind.ObjectMapper

4. Extend javax.ws.rs.core.Feature via MarshallingFeature like so:

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;

public class MarshallingFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(CustomJsonProvider.class, MessageBodyReader.class, MessageBodyWriter.class);
        return true;
    }
}

5. You need to register this custom provider like so, provided you configure your application via org.glassfish.jersey.server.ResourceConfig like so:

import org.glassfish.jersey.server.ResourceConfig;
...

public class MyApplication extends ResourceConfig {

    public MyApplication() {

        ...
        register(MarshallingFeature.class);
        ...
     }
 }

Other notes and observations:

  1. This solution applies whether you're using javax.ws.rs.core.Response to wrap your controller's responses or not.
  2. Please make sure you carefully take into consideration (copy/paste) the following code snippets since the only "non-mandatory" so to speak bits are the ones regarding the custom configuration of the com.fasterxml.jackson.databind.ObjectMapper.

@jcreason

Sorry for dropping the ball on this one @jcreason, I hope you're still curios. So I checked out the code from last year and this is what I came up w/ to provide a custom mapper.

The problem was that during feature initalization any custom object mappers get disabled by some code in

org.glassfish.jersey.jackson.JacksonFeature:77 (jersey-media-json-jackson-2.12.jar)

// Disable other JSON providers.
context.property(PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE, config.getRuntimeType()), JSON_FEATURE);

But this feature only gets registered by this component

org.glassfish.jersey.jackson.internal.JacksonAutoDiscoverable

if (!context.getConfiguration().isRegistered(JacksonFeature.class)) {
    context.register(JacksonFeature.class);
}

So what I did was to register my own feature which registeres my own object mapper provider and drops in a trip wire stopping org.glassfish.jersey.jackson.JacksonFeature from being registered and overriding my object mapper...

import com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper;
import com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper;

import org.glassfish.jersey.internal.InternalProperties;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;

public class MarshallingFeature implements Feature {

    private final static String JSON_FEATURE = MarshallingFeature.class.getSimpleName();

    @Override
    public boolean configure(FeatureContext context) {

      context.register(JsonParseExceptionMapper.class);
      context.register(JsonMappingExceptionMapper.class);
      context.register(JacksonJsonProviderAtRest.class, MessageBodyReader.class, MessageBodyWriter.class);

      final Configuration config = context.getConfiguration();
      // Disables discoverability of org.glassfish.jersey.jackson.JacksonFeature
      context.property(
          PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE,
                                                     config.getRuntimeType()), JSON_FEATURE);

      return true;
    }
}

And here is the custom object mapper provider...

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonJsonProviderAtRest extends JacksonJaxbJsonProvider {

    private static ObjectMapper objectMapperAtRest = new ObjectMapper();

    static {
        objectMapperAtRest.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapperAtRest.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapperAtRest.configure(SerializationFeature.INDENT_OUTPUT, true); // Different from default so you can test it :)
        objectMapperAtRest.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    }

    public JacksonJsonProviderAtRest() {
        super();
        setMapper(objectMapperAtRest);
    }
}
Krp answered 6/5, 2015 at 16:16 Comment(8)
3. Does not compile - cannot find symbol "objectMapperAtRest". Was this just meant to be "mapper" ?Birthplace
I was unable to get this to work. Getting org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class <Some Bean>.Birthplace
@Birthplace you're right, "objectMapperAtRest" was supposed to be "mapper"Krp
@Birthplace please add the maven dependency for jersey-media-json-jacksonKrp
@Birthplace during the time that has passed since I wrote this answer I've managed to improve the solution for this particular problem... if the solution still doesn't work for you then I'll add a new answer with the effective solution I'm currently usingKrp
Curious, what's your new solution?Chrystel
@Chrystel I hope you're still curios, sorry for dropping the ball on this one. So I checked out the code from last year and this is what I came up w/ to provide a custom mapper.Krp
Replacing {{jersey-media-json-jackson}} with {{jackson-jaxrs-json-provider}} is what fixed it for me...Unswear
S
36

I found a solution. I had to instantiate the Jackson Provider by myself and set my custom ObjectMapper. A working example can be found on GitHub: https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow-answer

I deleted my ObjectMapperResolver and modified my main method:

public class Main {
    public static void main(String[] args) {
        try {
            // create custom ObjectMapper
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);

            // create JsonProvider to provide custom ObjectMapper
            JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
            provider.setMapper(mapper);

            // configure REST service
            ResourceConfig rc = new ResourceConfig();
            rc.register(ExampleResource.class);
            rc.register(provider);

            // create Grizzly instance and add handler
            HttpHandler handler = ContainerFactory.createContainer(
                    GrizzlyHttpContainer.class, rc);
            URI uri = new URI("http://0.0.0.0:8080/");
            HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
            ServerConfiguration config = server.getServerConfiguration();
            config.addHttpHandler(handler, "/");

            // start
            server.start();
            System.in.read();

        } catch (ProcessingException | URISyntaxException | IOException e) {
            throw new Error("Unable to create HTTP server.", e);
        }
    }
}
Stuyvesant answered 20/9, 2013 at 13:22 Comment(6)
This is very helpful, but it still has a problem: Jersey auto-discovers the Jackson provider so it creates a (presumably unconfigured) provider, and then this code explicitly registers a (properly configured) provider, so Jersey ends up with two providers. I've illustrated this at (github.com/svenwltr/example-grizzly-jersey-jackson/issues/1). I don't know how to fix this. Any ideas?Dreddy
I believe you can turn auto-discovery off but then you lose it for everything.Schramke
The given example is working for me. Also, try user915662s solution.Stuyvesant
This worked for me when I was trying to run some test cases with protos. NicePlica
you can disable autodiscovery with resourceConfig.addProperties(Collections.singletonMap(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true))Kolinsky
I did not need to explicitly reference the ObjectMapperContext. Just make sure you have it under a package, say com.yourcompany.a.b.c and then ensure you have this instruction in your ResourceConfig (or main): packages("com.yourcompany.a.b.c");Forayer
M
9

I figured this out, based on a bit of tinkering.

The issue appears to be in Jersey's feature autodetection mechanism. If you rely on Jersey to load the JacksonJaxbJsonProvider, then the custom context provider for your ObjectMapper is ignored. If, instead, you manually register the feature, it works. I hypothesize that this has to do with the autodetected provider being loaded into a different context scope, but as for a solution, here's what I ended up with. Note that I wrapped it into a feature, you should be able to register it directly with your application without a problem.

public final class RequestMappingFeature implements Feature {

    @Override
    public boolean configure(final FeatureContext context) {

        context.register(ObjectMapperProvider.class);

        // If you comment out this line, it stops working.
        context.register(JacksonJaxbJsonProvider.class);

        return true;
    }
}

UPDATE November 2017: Things have changed a bit in the Jersey2 world. If the above doesn't work, try this:

The new method of providing your own ObjectMapper now looks like this:

public final class JacksonFeature implements Feature {

    private static final ObjectMapper MAPPER;

    static {

        // Create the new object mapper.
        MAPPER = new ObjectMapper();

        // Enable/disable various configuration flags.
        MAPPER.configure(
                DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);

        // ... Add your own configurations here.

    }
    @Override
    public boolean configure(final FeatureContext context) {
        JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(
                MAPPER, DEFAULT_ANNOTATIONS);
        context.register(provider);

        return true;
    }
}
Mope answered 3/3, 2014 at 16:57 Comment(3)
Adding this feature just gives me "WARNING: No resource methods have been found for resource class ... RequestMappingFeature"Bricole
"new method" works for me on Jersey 2.27 and Jackson 2.9Whyalla
Works on Jersey 2.23.2 and Jackson 2.5.4, and is much simpler than the other solutions AFAICT. To make this dead simple for next time, DEFAULT_ANNOTATIONS is a field on JacksonJaxbJsonProvider, and you ResourceConfig.register(JacksonFeature.class).Overprize
B
8

Please do this:

1) add pom.xml dependency

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.2</version>
</dependency>

2) register JacksonFeature in the Main.java

public class Main {

    public static void main(String[] args) {
        try {
            ResourceConfig rc = new ResourceConfig();
            rc.register(ExampleResource.class);
            rc.register(ObjectMapperResolver.class);
            rc.register(JacksonFeature.class);

            HttpHandler handler = ContainerFactory.createContainer(
                    GrizzlyHttpContainer.class, rc);

            URI uri = new URI("http://0.0.0.0:8080/");

            HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);

            ServerConfiguration config = server.getServerConfiguration();
            config.addHttpHandler(handler, "/");

            server.start();
            System.in.read();

        } catch (ProcessingException | URISyntaxException | IOException e) {
            throw new Error("Unable to create HTTP server.", e);
        }
    }
}

3) Use org.codehaus.jackson.map.ObjectMapper in your resource

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperResolver() {
        System.out.println("new ObjectMapperResolver()");
        mapper = new ObjectMapper();
        mapper.enable(Feature.INDENT_OUTPUT);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        System.out.println("ObjectMapperResolver.getContext(...)");
        return mapper;
    }
}
Breather answered 18/9, 2013 at 23:6 Comment(3)
That's working, but than I have to downgrade to Jackson 1.9. I would prefer Jackson 2.1. Sorry, I didn't mention that.Stuyvesant
For those of us coming to this issue late - it still occurs in latest Jackson 2.x (v2.3). This answer uses an older Jersey/Jackson connector lib that relies on Jackson 1.x. Whole issue here is that what worked in Jackson 1.x does NOT work in 2.x. You must manually register the Jackson provider (turning off METAINF auto-discover) for now.Deposit
I don't get why there's a @Produces annotation on this class. This is not a Jersey resource..!Dougall
F
7

As it took me some hours to get this working with Java EE7 and Glassfish4, here's my solution:

@javax.ws.rs.ApplicationPath("withJackson")
public class ApplicationConfig extends Application {

    private static final Logger log = java.util.logging.Logger.getLogger(ApplicationConfig.class.getName());

    @Override
    public Set<Object> getSingletons() {
        Set<Object> set = new HashSet<>();
        log.log(Level.INFO, "Enabling custom Jackson JSON provider");
        set.add(new JacksonJsonProvider().configure(SerializationFeature.INDENT_OUTPUT, true));
        return set;
    }

    @Override
    public Map<String, Object> getProperties() {
        Map<String, Object> map = new HashMap<>();
        log.log(Level.INFO, "Disabling MOXy JSON provider");
        map.put("jersey.config.disableMoxyJson.server", true);
        return map;
    }

    @Override
public Set<Class<?>> getClasses() {
    Set<Class<?>> resources = new java.util.HashSet<>();
    addRestResourceClasses(resources);
    return resources;
}

/**
 * Do not modify addRestResourceClasses() method.
 * It is automatically populated with
 * all resources defined in the project.
 * If required, comment out calling this method in getClasses().
 */
private void addRestResourceClasses(Set<Class<?>> resources) {
    resources.add(com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper.class);
    resources.add(com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper.class);
    resources.add(com.fasterxml.jackson.jaxrs.json.JsonMappingExceptionMapper.class);
    resources.add(com.fasterxml.jackson.jaxrs.json.JsonParseExceptionMapper.class);
    resources.add(de.lathspell.java_test_ee7_json.Api.class);
    resources.add(de.lathspell.java_test_ee7_json.with_jackson.MyExceptionMapper.class);
}

The only relevant POM dependencies are:

    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>2.2.3</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.2.3</version>
    </dependency>

    <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-web-api</artifactId>
        <version>7.0</version>
        <scope>provided</scope>
    </dependency>
Filum answered 2/4, 2014 at 2:57 Comment(2)
+1 for map.put("jersey.config.disableMoxyJson.server", true);, it got me struggling for hours. Thanks!Abstention
Thanks, this is what worked for me ( SDK8, Jersey 2.10.x ) just a remark, disabling Moxy is not required if it's not added in the first time.Ardis
H
7

From the Jersey 2.17 docs: https://eclipse-ee4j.github.io/jersey.github.io/documentation/2.17/media.html#jackson-registration

In the application

@ApplicationPath("/")
public class MyApplication extends ResourceConfig {
  public MyApplication() {
    register(JacksonFeature.class);
    // This is the class that you supply, Call it what you want
    register(JacksonObjectMapperProvider.class);
    //...
  }
}

Edit, forgot to add the JacksonObjectMapperProvider that you supply in register(..):

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper>{
  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapperProvider() {
     defaultObjectMapper = createDefaultMapper();
  }

  @Override
  public ObjectMapper getContext(Class<?> type) {return defaultObjectMapper;}

   public static ObjectMapper createDefaultMapper() {
      final ObjectMapper jackson = new ObjectMapper();
      // any changes to the ObjectMapper is up to you. Do what you like.
      // The ParameterNamesModule is optional,
      // it enables you to have immutable POJOs in java8
      jackson.registerModule(new ParameterNamesModule());
      jackson.enable(SerializationFeature.INDENT_OUTPUT);
      jackson.disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
      jackson.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
      return jackson;
   }
}
Hashim answered 10/5, 2015 at 22:19 Comment(3)
This is not an answer to the question. I think you missed the point that he is trying to register a custom mapper, not the default one.Dacy
It works. What is have in new ParameterNamesModule() object ?Alenaalene
The createDefaultMapper() method provides the custom objectmapper. Create whatever ObjectMapper and configure it the way you want it to work in that method. ParameterNamesModule (github.com/FasterXML/jackson-modules-java8/tree/master/…) is just an example of what you can configure.Hashim
P
2

With Jackson 2.7, making this did not worked:

public class MyApplication extends ResourceConfig {
    public MyApplication() {
    register(MyObjectMapperProvider.class);
}}

MyObjectMapperProvider constructor was called, but getContext() was never called.

Registering MyObjectMapperProvider in super() constructor make it work:

public class MyApplication extends ResourceConfig {
   public MyApplication() {
       super(
            // register Jackson ObjectMapper resolver
            MyObjectMapperProvider.class
       );
}}

See this Jersey example code.

Province answered 8/4, 2016 at 20:17 Comment(0)
S
1

If you are searching for a solution for Jersey 3+. You need the Jakarta Provider Dependency for Jackson. Like Filip tells in his answer, please be aware that you only have one json provider, the jaxrs provider in your dependencies. (Make sure your pom.xml doesn't have the "jersey-media-json-jackson" provider included.

   <dependency>
     <groupId>com.fasterxml.jackson.jakarta.rs</groupId>
     <artifactId>jackson-jakarta-rs-json-provider</artifactId>
   </dependency>

Based on the code example from from krotscheck, you can now create a JacksonFeature for Jersey3+.

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.jakarta.rs.json.JacksonXmlBindJsonProvider;

import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;

public final class JacksonFeature implements Feature {

    private static final ObjectMapper MAPPER = new ObjectMapper()
            .configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true)
            .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    @Override
    public boolean configure(final FeatureContext context) {
        JacksonXmlBindJsonProvider provider = new JacksonXmlBindJsonProvider(MAPPER,
                JacksonXmlBindJsonProvider.DEFAULT_ANNOTATIONS);
        context.register(provider);

        return true;
    }
}

Register this Feature in the Client:

Client client = ClientBuilder.newClient().register(JacksonFeature.class)
Soviet answered 21/6, 2023 at 21:55 Comment(1)
This was really helpful for me. Especially the info that "jersey-media-json-jackson" must not be on the classpath to get this working.Heida

© 2022 - 2024 — McMap. All rights reserved.