2013-12-13 114 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

相關問題