Java 在 Jersey 中使用 Jackson ObjectMapper

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20563640/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:50:19  来源:igfitidea点击:

Using Hymanson ObjectMapper with Jersey

javajsonspringrestjersey-2.0

提问by jcstockdale

I'm using Jersey 2.4 to create a simple REST interface that serves up a JSON object. My problem is that I'm trying to use the fasterxml Hymanson annotations to control the output and this is not working for me. I have put the annotations into my bean class but they are ignored.

我正在使用 Jersey 2.4 创建一个简单的 REST 接口来提供一个 JSON 对象。我的问题是我正在尝试使用 fastxml Hymanson 注释来控制输出,但这对我不起作用。我已将注释放入我的 bean 类中,但它们被忽略了。

When I explicitly create an ObjectMapper and use this to stringify the Java bean, I get the output that I want, which respects the Hymanson annotations. However, I would prefer that I don't have to do this step so that my resource class can simply return the bean and the Jersey framework takes care of stringifying it.

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

I have tried to solve this using the answer from Custom ObjectMapper with Jersey 2.2 and Hymanson 2.1, however, this does not appear to work for me. I see that the ContextResolver is created but it is never called.

我试图使用来自带有 Jersey 2.2 和 Hymanson 2.1 的 Custom ObjectMapper的答案来解决这个问题,但是,这似乎对我不起作用。我看到 ContextResolver 已创建,但从未调用过。

I have also spent many hours trying to solve this apparently simple problem. I have stripped this down to a very simple test case, which is shown below. I would appreciate any help at all in resolving this.

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

Resource Java class:

资源 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);
    }
}

web.xml:

网页.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:

POM依赖:

<dependencies>
    <dependency>
        <groupId>com.fasterxml.Hymanson.jaxrs</groupId>
        <artifactId>Hymanson-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-Hymanson</artifactId>
        <version>2.4.1</version>
    </dependency>
</dependencies>

回答by thermz

There are a lot of ways to integrate Hymansonwith Jax-rsJersey implementation.

有很多方法可以将HymansonJax-rsJersey 实现集成。

If you take a look to Mkyong tutorial: http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-Hymanson/It seems that you should also pass the "POJOMappingFeature" -> true in the init params in web.xml. I think this works for Jersey 1.8

如果你看看 Mkyong 教程:http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-Hymanson/ 看来你也应该通过“POJOMappingFeature”-> true web.xml 中的初始化参数。我认为这适用于Jersey 1.8

If you take a look to official Jersey documentation instead: https://jersey.java.net/nonav/documentation/latest/user-guide.html#json.HymansonIt seems that you should implements a Jax-rs provider and add that provider to your application resources

如果您查看官方 Jersey 文档: https://jersey.java.net/nonav/documentation/latest/user-guide.html#json.Hymanson似乎您应该实现一个 Jax-rs 提供程序并添加它应用程序资源的提供者

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>

They provide you an example of how to do this https://github.com/jersey/jersey/blob/2.4.1/examples/json-Hymanson/src/main/java/org/glassfish/jersey/examples/Hymanson/MyObjectMapperProvider.java

他们为您提供了如何执行此操作的示例 https://github.com/jersey/jersey/blob/2.4.1/examples/json-Hymanson/src/main/java/org/glassfish/jersey/examples/Hymanson/ MyObjectMapperProvider.java

I used this way and that solved my problems, and Hymanson annotations are correctly scanned by Hymanson provider.

我使用这种方式解决了我的问题,并且 Hymanson 提供程序正确扫描了 Hymanson 注释。



Off topic I suggest you to use this syntax in your bean to initialize map:

题外话我建议你在你的 bean 中使用这个语法来初始化地图:

import static java.util.Arrays.asList;

public static class Foobar {
        @JsonIgnore
        private String foo = "foo";
        private String baa = "baa";
        private Map<String, List<? extends Number>> map = new HashMap<>(){{
            put("even", asList(2, 4, 6, 8, 10));
            put("odd", asList(1, 3, 5, 7, 9));
            put("float", asList(1.1F, 2.2F, 3.3F));
        }};

        public Foobar() {
        }

        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;
        }
    }

回答by robert_difalco

Unfortunately everyone makes this much harder than it needs to be. The Jersey team in their wisdom decided to integrate Hymanson 1.9, so their stuff wont' help you.

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

But it was pretty easy for me. Just do this:

但这对我来说很容易。只需这样做:

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

Now GET RID OF THIS:

现在摆脱这个:

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

Then in your web.xml change this line:

然后在您的 web.xml 中更改这一行:

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

To be:

成为:

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

That should do it.

那应该这样做。

回答by Patrick Favre

EDIT: Don't use the old approach belowas it produces bugs (at least with with android device, see EDIT2 for more details). As of my tests, Jersey v2.6 seems to fix the problem with the @Provide, which approach did not work. I was able to get it work with this simple provider:

编辑:不要使用下面旧方法,因为它会产生错误(至少对于 android 设备,有关更多详细信息,请参阅 EDIT2)。在我的测试中,Jersey v2.6 似乎解决了 的问题@Provide,哪种方法不起作用。我能够使用这个简单的提供程序让它工作:

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

So please don't use my hack from below.

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



OLD APPROACH

旧方法

Using

使用

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>

was not working for me (Jersey 2.4 & Hymanson 2.3) and maybe this is due to a in the Hymanson provider reported bug in the code where the ContextResolvershould be registered in HymansonJsonProvider.java(2.3rc1):

对我不起作用(Jersey 2.4 & Hymanson 2.3),这可能是由于 Hymanson provider 报告了代码中的错误,ContextResolver应该在HymansonJsonProvider.java(2.3rc1) 中注册:

 @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;
}

But at least I cannot access https://jersey.dev.java.net/issues/show_bug.cgi?id=288, so I don't know what this bug is about.

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

But I found a workaround (a hack if you so will). Just extend HymansonJsonProviderwith the proper annotation and return your ObjectMapperlike this:

但我找到了一个解决方法(如果你愿意的话,这是一个黑客)。只需HymansonJsonProvider使用适当的注释进行扩展,然后ObjectMapper像这样返回:

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

No need to do anything it will register itself (check with log, it will register the first time you access a json rest service). This is now working for me, not elegant, but I gave up.

不需要做任何事情它会注册自己(检查日志,它会在你第一次访问 json 休息服务时注册)。这现在对我有用,不优雅,但我放弃了。

EDIT: Use with caution - Im experiencing a bug maybe related to this hack: Android volley cannot send a POST/PUT request with a request body, always getting 400 from the framework, I will investigate and report my findings.

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

EDIT2: This hack was indeed responsible for a generic 400 whenever an Android app with volley and OKHTTPclient tried tried to do a POST or PUT request so don't use this - in my test jersey 2.6 seems to fix this so you can use @Provideapproach

EDIT2:每当带有 volley 和OKHTTP客户端的 Android 应用程序尝试执行 POST 或 PUT 请求时,此 hack 确实会导致通用 400,所以不要使用它 - 在我的测试球衣 2.6 中似乎解决了这个问题,因此您可以使用@Provide方法

回答by Nthalk

Using Jersey 2.13, you can force the @Providerto use the same ObjectMappershould only create one ObjectMapper:

使用Jersey 2.13,您可以强制@Provider使用相同的ObjectMapper应该只创建一个ObjectMapper

package com.example.api.environment.configs;

import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.SerializationFeature;
import com.fasterxml.Hymanson.datatype.joda.JodaModule;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class HymansonConfig implements ContextResolver<ObjectMapper> {

  private final ObjectMapper objectMapper;

  public HymansonConfig() {
    objectMapper = new ObjectMapper()
          .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
          .registerModule(new JodaModule());
  };

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

I use this to @Autowirein my ObjectMapperto generate json-schemadocuments on the fly.

我用这个来@AutowireObjectMapper生成json-schema的飞行文件。

回答by Shashi Dk

add on each model class like this

像这样添加每个模型类

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Event {
  -----
  --
}

it has worked for me but i have used codehus Hymanson jars

它对我有用,但我使用过codehus Hymanson jars