将 Jackson ObjectMapper 与 Jersey 一起使用

2022-09-04 00:35:45

我正在使用 Jersey 2.4 创建一个简单的 REST 接口来提供 JSON 对象。我的问题是,我试图使用更快的xml Jackson注释来控制输出,这对我不起作用。我已经把注释放到我的豆类中,但它们被忽略了。

当我显式创建一个 ObjectMapper 并使用它来字符串化 Java Bean 时,我得到了我想要的输出,它尊重 Jackson 注释。但是,我宁愿我不必执行此步骤,以便我的资源类可以简单地返回 Bean,而 Jersey 框架负责将其字符串化。

我试图使用带有泽西岛2.2和Jackson 2.1的自定义对象映射器的答案来解决此问题,但是,这似乎对我不起作用。我看到上下文解析器被创建,但它从未被调用。

我也花了很多时间试图解决这个看似简单的问题。我已将其简化为一个非常简单的测试用例,如下所示。如果能帮助解决这个问题,我将不胜感激。

资源 Java 类:

@Path("resource")
public class MainResource {

    public static class Foobar {
        @JsonIgnore
        private String foo = "foo";
        private String baa = "baa";
        private Map<String, List<? extends Number>> map = new HashMap<>();

        public Foobar() {
            map.put("even", Arrays.asList(new Integer[] { 2, 4, 6, 8, 10 }));
            map.put("odd", Arrays.asList(new Integer[] { 1, 3, 5, 7, 9 }));
            map.put("float", Arrays.asList(new Float[] { 1.1F, 2.2F, 3.3F }));
        }

        public String getFoo() {
            return foo;
        }

        public void setFoo(String foo) {
            this.foo = foo;
        }

        public String getBaa() {
            return baa;
        }

        public void setBaa(String baa) {
            this.baa = baa;
        }

        @JsonAnyGetter
        public Map<String, List<? extends Number>> getMap() {
            return map;
        }

        public void setMap(Map<String, List<? extends Number>> map) {
            this.map = map;
        }
    }

    private ObjectMapper om = new ObjectMapper();

    @GET
    @Path("get-object")
    @Produces(MediaType.APPLICATION_JSON)
    public Foobar getObject() {
        // In this method, I simply return the bean object but the WRONG JSON syntax is generated.
        return new Foobar();
    }

    @GET
    @Path("get-string")
    @Produces(MediaType.APPLICATION_JSON)
    public String getString() throws JsonProcessingException {
        // This method returns the RIGHT JSON syntax but I don't want to have to explicitly use the ObjectMapper.
        Foobar foobar = new Foobar();
        return om.writeValueAsString(foobar);
    }
}

网页.xml:

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <module-name>sample</module-name>

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>ie.cit.nimbus.sample</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

POM 依赖关系:

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>2.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.4.1</version>
    </dependency>
</dependencies>

答案 1

编辑:不要使用下面的旧方法,因为它会产生错误(至少在Android设备上,请参阅EDIT2以获取更多详细信息)。在我的测试中,泽西 v2.6 似乎修复了 的问题,这种方法不起作用。我能够让它与这个简单的提供商一起工作:@Provide

@Provider
public class JerseyMapperProvider implements ContextResolver<ObjectMapper> {
    private static ObjectMapper apiMapper = ObjectMapperManager.createMapperForApi();
    @Override
    public ObjectMapper getContext(Class<?> type)
    {
        return apiMapper;
    }
}

所以请不要使用我下面的黑客。


旧方法

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>

不适合我(泽西岛2.4和杰克逊2.3),也许这是由于杰克逊提供商报告的代码中的错误,应该在(2.3rc1)中注册:ContextResolverJacksonJsonProvider.java

 @Override
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType)
{
    if (_providers != null) {
        ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
        /* Above should work as is, but due to this bug
         *   [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
         * in Jersey, it doesn't. But this works until resolution of
         * the issue:
         */
        if (resolver == null) {
            resolver = _providers.getContextResolver(ObjectMapper.class, null);
        }
        if (resolver != null) {
            return resolver.getContext(type);
        }
    }
    return null;
}

但至少我无法访问 https://jersey.dev.java.net/issues/show_bug.cgi?id=288,所以我不知道这个错误是什么。

但是我找到了一个解决方法(如果你愿意的话,这是一个黑客)。只需使用正确的注释进行扩展,然后返回如下所示的注释:JacksonJsonProviderObjectMapper

@Provider
@Consumes(MediaType.APPLICATION_JSON) // NOTE: required to support "non-standard" JSON variants
@Produces(MediaType.APPLICATION_JSON)
public class JacksonHackProvider extends JacksonJsonProvider {
    @Override
    protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType) {
        return new MyCustomObjectMapper();
    }
}

无需执行任何操作,它将自行注册(检查日志,它将在您第一次访问json rest服务时注册)。这现在为我工作,不优雅,但我放弃了。

编辑:谨慎使用 - 我遇到一个可能与此黑客有关的错误:Android凌空抽射无法发送带有请求正文的POST / PUT请求,始终从框架获得400,我将调查并报告我的发现。

编辑2:这个黑客确实负责一个通用的400,每当一个Android应用程序与凌空和客户端尝试做一个POST或PUT请求,所以不要使用这个 - 在我的测试球衣2.6似乎修复了这个问题,所以你可以使用方法OKHTTP@Provide


答案 2

不幸的是,每个人都使这比需要的要困难得多。泽西岛团队在他们的智慧中决定整合杰克逊1.9,所以他们的东西不会帮助你。

但这对我来说很容易。只需执行以下操作:

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

现在摆脱这个:

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

然后在您的网络中.xml更改以下行:

<param-value>ie.cit.nimbus.sample</param-value>

成为:

<param-value>ie.cit.nimbus.sample,com.fasterxml.jackson.jaxrs.json</param-value>

这应该可以做到。