簡體   English   中英

使用Jackson ObjectMapper和Jersey

[英]Using Jackson ObjectMapper with 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<? 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>

編輯 :不要使用下面舊方法,因為它產生錯誤(至少與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;
    }
}

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


老方法

運用

@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休息服務時注冊)。 現在這對我有用,不優雅,但我放棄了。

編輯:謹慎使用 - 我遇到一個可能與此黑客有關的錯誤:Android凌空無法向請求正文發送POST / PUT請求,總是從框架中獲得400,我將調查並報告我的發現。

編輯2:這個黑客確實負責一個通用400,每當一個Android應用程序與齊射和OKHTTP客戶端嘗試做一個POST或PUT請求所以不要使用它 - 在我的測試球衣2.6似乎解決這個,所以你可以使用@Provide方法

不幸的是,每個人都比實際需要的更難。 澤西隊以他們的智慧決定整合傑克遜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>

然后在你的web.xml中改變這一行:

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

成為:

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

應該這樣做。

使用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文檔。

有很多方法可以將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<ObjectMapper>

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

我用這種方式解決了我的問題,傑克遜提供商正確地掃描了Jackson注釋。


關於主題我建議你在bean中使用這個語法初始化map:

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

像這樣添加每個模型類

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

它對我有用但我使用了codehus jackson罐子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM