복붙노트

[SPRING] Jersey와 함께 Jackson ObjectMapper 사용하기

SPRING

Jersey와 함께 Jackson ObjectMapper 사용하기

Jersey 2.4를 사용하여 JSON 객체를 처리하는 간단한 REST 인터페이스를 작성한다. 내 문제는 내가 fasterxml 잭슨 주석을 사용하여 출력을 제어하려고하는데 이것은 나를 위해 작동하지 않습니다. 주석을 내 bean 클래스에 넣었지만 무시됩니다.

ObjectMapper를 명시 적으로 작성하고이를 Java bean을 문자열 화하기 위해 사용할 때 잭슨 주석을 존중하는 원하는 출력을 얻습니다. 그러나이 단계를 수행 할 필요가 없기 때문에 리소스 클래스가 빈을 반환하고 Jersey 프레임 워크가 문자열을 처리 할 수 ​​있습니다.

저지 2.2 및 잭슨 2.1 사용자 지정 ObjectMapper 응답을 사용하여이 문제를 해결하기 위해 노력했지만이 나를 위해 작동하지 않습니다. 나는 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-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. ==============================

    1.편집 : 버그를 생산으로 아래의 기존 접근 방식을 사용하지 마십시오 (적어도 안드로이드 장치와 자세한 내용은 EDIT2 참조). Jersey v2.6이 내 테스트에서 @Provide를 사용하여 문제를 해결 한 것으로 보입니다.이 접근 방식은 작동하지 않았습니다. 나는이 간단한 공급자와 함께 작동시킬 수 있었다.

    편집 : 버그를 생산으로 아래의 기존 접근 방식을 사용하지 마십시오 (적어도 안드로이드 장치와 자세한 내용은 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>
    

    나 (Jersey 2.4 및 Jackson 2.3)에서 작동하지 않았다면 Jackson 제공자가 JacksonJsonProvider.java (2.3rc1)에 ContextResolver를 등록해야하는 코드 버그를보고했기 때문일 수 있습니다.

     @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에 액세스 할 수 없으므로이 버그에 대해 알지 못합니다.

    하지만 해결 방법을 찾았습니다 (그렇게한다면 해킹). 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 service에 액세스 할 때 등록됩니다). 이것은 지금 나를 위해 일하고있다, 우아하지 않다. 그러나 나는 단념했다.

    편집 : 신중하게 사용 -이 해킹과 관련된 버그가 발생했습니다 : Android 발리볼은 요청 본문과 함께 POST / PUT 요청을 보낼 수 없으며 프레임 워크에서 항상 400 개가 나오면 조사하고보고 할 것입니다.

    EDIT2 : 발리와 OKHTTP 클라이언트가있는 Android 앱이 POST 또는 PUT 요청을 시도 할 때마다이 해킹이 실제로 범용 400에 대한 책임이 있습니다.이 테스트를 사용하면 제니스 2.6에서이 문제를 해결할 수 있으므로 @ 접근법 제공

  2. ==============================

    2.불행히도 모든 사람들이 필요 이상으로 힘들게 만듭니다. 저지 팀은 잭슨 1.9를 통합하기로 결정했기 때문에 그들의 도움을받지 못했습니다.

    불행히도 모든 사람들이 필요 이상으로 힘들게 만듭니다. 저지 팀은 잭슨 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>
    

    그렇게해야합니다.

  3. ==============================

    3.Jersey 2.13을 사용하면 @Provider가 동일한 ObjectMapper를 사용하도록 강제 할 수 있습니다. 하나의 ObjectMapper 만 작성해야합니다.

    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 문서를 즉시 생성합니다.

  4. ==============================

    4.Jackson을 Jax-rs Jersey 구현과 통합하는 데는 여러 가지 방법이 있습니다.

    Jackson을 Jax-rs Jersey 구현과 통합하는 데는 여러 가지 방법이 있습니다.

    Mkyong 튜토리얼 : http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/ web.xml의 init params에서도 "POJomappingFeature"-> true를 전달해야합니다. 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 주석은 Jackson 제공 업체가 올바르게 스캔했습니다.

    주제 끄기 빈에서이 구문을 사용하여 맵을 초기화하는 것이 좋습니다.

    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;
            }
        }
    
  5. ==============================

    5.이처럼 각 모델 클래스에 추가하십시오.

    이처럼 각 모델 클래스에 추가하십시오.

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

    그것은 나를 위해 일했다. 그러나 나는 codehaus jackson jars를 사용했다.

  6. from https://stackoverflow.com/questions/20563640/using-jackson-objectmapper-with-jersey by cc-by-sa and MIT license