使用Jackson ObjectMapper和Jersey

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

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

我尝试使用Custom ObjectMapper与Jersey 2.2和Jackson 2.1的答案来解决这个问题,但是,这似乎对我没有用。 我看到ContextResolver已创建,但从未调用过。

我也花了很多时间试图解决这个看似简单的问题。 我把它剥离到一个非常简单的测试用例,如下所示。 在解决这个问题上,我将不胜感激。

资源Java类:

@Path("resource") public class MainResource { public static class Foobar { @JsonIgnore private String foo = "foo"; private String baa = "baa"; private Map<String, List> 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> getMap() { return map; } public void setMap(Map<String, List> 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中:

  sample  Jersey Web Application org.glassfish.jersey.servlet.ServletContainer  jersey.config.server.provider.packages ie.cit.nimbus.sample    Jersey Web Application /*   

POM依赖:

   com.fasterxml.jackson.jaxrs jackson-jaxrs-json-provider 2.3.0   org.glassfish.jersey.ext jersey-spring3 2.4.1   org.glassfish.jersey.media jersey-media-json-jackson 2.4.1   

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

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

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


老方法

运用

 @Provider public class MyObjectMapperProvider implements ContextResolver 

我没有为我工作(Jersey 2.4和Jackson 2.3),也许这是因为jackson提供商在代码中报告了一个错误,其中JacksonJsonProvider.java应该在JacksonJsonProvider.java (2.3rc1)中注册:

  @Override protected ObjectMapper _locateMapperViaProvider(Class type, MediaType mediaType) { if (_providers != null) { ContextResolver 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 ,所以我不知道这个bug到底是什么。

但是我找到了一个解决方法(如果你愿意,那就是黑客)。 只需使用正确的注释扩展JacksonJsonProvider并返回您的ObjectMapper如下所示:

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

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

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

编辑2:这个黑客确实负责通用400每当一个Android应用程序与齐射和OKHTTP客户端试图尝试做POST或PUT请求所以不要使用它 – 在我的测试jersey2.6似乎解决这个问题,所以你可以使用@Provide方法

不幸的是,每个人都比实际需要的更难。 泽西队以他们的智慧决定整合jackson1.9,所以他们的东西不会帮助你。

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

   com.fasterxml.jackson.core jackson-databind 2.3.0   com.fasterxml.jackson.jaxrs jackson-jaxrs-json-provider 2.3.0  

现在获得这个:

  org.glassfish.jersey.media jersey-media-json-jackson 2.4.1  

然后在你的web.xml中改变这一行:

 ie.cit.nimbus.sample 

成为:

 ie.cit.nimbus.sample,com.fasterxml.jackson.jaxrs.json 

应该这样做。

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

 package com.example.api.environment.configs; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.joda.JodaModule; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; @Provider public class JacksonConfig implements ContextResolver { private final ObjectMapper objectMapper; public JacksonConfig() { objectMapper = new ObjectMapper() .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .registerModule(new JodaModule()); }; @Override public ObjectMapper getContext(Class type) { return objectMapper; } } 

我在我的ObjectMapper使用它来@Autowire来动态生成json-schema文档。

有很多方法可以将jacksonJax-rs Jersey实现集成。

如果你看看Mkyong教程: http ://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/看来你也应该传递“POJOMappingFeature” – > true in web.xml中的init参数。 我认为这适用于Jersey 1.8

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

 @Provider public class MyObjectMapperProvider implements ContextResolver 

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

我用这种方式解决了我的问题,jackson提供商正确地扫描了Jackson注释。


关于主题我建议你在bean中使用这个语法来初始化map:

 import static java.util.Arrays.asList; public static class Foobar { @JsonIgnore private String foo = "foo"; private String baa = "baa"; private Map> 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> getMap() { return map; } public void setMap(Map> map) { this.map = map; } } 

像这样添加每个模型类

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

它对我有用,但我用过codehus jacksonjar子