2013-12-13 53 views
13

我使用Jersey 2.4创建了一个提供JSON对象的简单REST接口。我的问题是,我试图使用更快的xml杰克逊注释来控制输出,这不适合我。我已将注释放入我的bean类中,但它们被忽略。使用Jackson ObjectMapper和Jersey

当我显式创建一个ObjectMapper并使用它来对Java bean进行字符串化时,我得到了我想要的输出,它尊重了Jackson注释。然而,我宁愿我不必这样做,这样我的资源类就可以简单地返回bean,而Jersey框架负责对它进行字符串化。

我试图解决这个使用从Custom ObjectMapper with Jersey 2.2 and 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<? 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:

<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

有很多的方法来杰克逊整合与贾克斯-rs Jersey im plementation。

如果你看看Mokong教程:http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/ 看来你还应该在web.xml中的init params中传递“POJOMappingFeature” - > true。 我想这样的作品为泽西1.8

如果你看一下官方的球衣文件来代替: https://jersey.java.net/nonav/documentation/latest/user-guide.html#json.jackson 看来你应该实现JAX-RS提供商和供应商添加到您的应用程序资源

@Provider 
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> 

他们为您提供的如何做到这一点 https://github.com/jersey/jersey/blob/2.4.1/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java

我用这种方式一个实例,解决了我的概率杰姆逊提供商正确扫描了lems和Jackson注释。


题外话,我建议你使用这个语法在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; 
     } 
    } 
7

不幸的是每个人都会比它需要这个更难。泽西队的智慧决定整合杰克逊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> 

现在得到这个RID:

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

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

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

为:

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

这应该做到这一点。

+0

谢谢。我尝试过,但似乎没有任何区别。它仍然忽略fasterxml注释并产生错误的JSON输出。还有什么我需要改变? – jcstockdale

+0

你能给我你正在尝试使用的具体注释吗?我将设置一个测试用例。 –

+0

你的意思是JsonIgnore注释?尝试把它放在吸气剂和/或制定者而不是私人领域。 –

11

编辑:请勿使用以下的老办法,因为它产生的错误(至少与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; 
    } 
} 

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


OLD APPROACH

使用

@Provider 
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> 

是不是为我工作(泽西岛2.4 &杰克逊2.3),也许这是由于在杰克逊提供报告的bug,在代码ContextResolver应注册在JacksonJsonProvider.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; 
} 

但至少我不能访问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(); 
    } 
} 

无需做任何事情,将自己注册(请与日志,它会在你第一次访问一个JSON REST服务注册)。这现在对我来说很不起眼,但我放弃了。

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

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

+0

您正在使用的这个'ObjectMapperManager'类是什么?这是你自己的吗? –

+0

只是一个自定义工厂类,你可以用例如替换它。 '新的ObjectMapper()' – for3st

+0

只是一个自定义的工厂类,你可以用例如替换它。 '新的ObjectMapper()' – for3st

5

使用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<ObjectMapper> { 

    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文件。

+0

你可以使用解决方案,没有春天感染所有其他类? –

+0

@AndréSchild:完成。 – Nthalk

-1

加上每个模型类这样

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

它为我的工作,但我已经使用codehus杰克逊罐子

+0

请参考链接:http://stackoverflow.com/help/how-to-answer – Vikrant

相关问题