2013-12-13 12 views
13

Ich verwende Jersey 2.4, um eine einfache REST-Schnittstelle zu erstellen, die ein JSON-Objekt bereitstellt. Mein Problem ist, dass ich versuche, die fasterxml Jackson Annotationen zu verwenden, um die Ausgabe zu steuern, und das funktioniert nicht für mich. Ich habe die Anmerkungen in meine Bean-Klasse geschrieben, aber sie werden ignoriert.Verwenden von Jackson ObjectMapper mit Jersey

Wenn ich explizit einen ObjectMapper erstellen und diese verwenden, um die Java-Bean zu stringieren, bekomme ich die Ausgabe, die ich will, die die Jackson-Anmerkungen respektiert. Ich würde jedoch bevorzugen, dass ich diesen Schritt nicht tun muss, so dass meine Ressourcenklasse einfach die Bohne zurückgeben kann und das Jersey-Framework dafür sorgt, dass sie stringifiziert wird.

Ich habe versucht, dies zu lösen mit der Antwort von Custom ObjectMapper with Jersey 2.2 and Jackson 2.1, scheint dies jedoch nicht für mich arbeiten. Ich sehe, dass der ContextResolver erstellt wird, aber nie aufgerufen wird.

Ich habe auch viele Stunden damit verbracht, dieses scheinbar einfache Problem zu lösen. Ich habe dies auf einen sehr einfachen Testfall reduziert, der unten gezeigt wird. Ich würde jede Hilfe bei der Lösung dieses Problems zu schätzen wissen.

Ressourcen Java-Klasse:

@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 Abhängigkeiten:

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

Antwort

1

Es gibt eine Menge Möglichkeiten, jackson mit Jax zu integrieren -rs Jersey im Plementation.

Wenn Sie einen Blick auf Mkyong Tutorial werfen: http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/ Es scheint, dass Sie auch die "POJOMappingFeature" -> true in den init-Parametern in web.xml übergeben sollten. Ich denke, das für Jersey 1.8

funktioniert Wenn Sie einen Blick auf offizielle Jersey Dokumentation nehmen statt: https://jersey.java.net/nonav/documentation/latest/user-guide.html#json.jackson Es scheint, dass Sie einen JAX-RS-Provider implementiert sollen und dass die Anbieter auf Ihre Anwendungsressourcen hinzufügen

@Provider 
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> 

Sie bieten Ihnen ein Beispiel, wie diese https://github.com/jersey/jersey/blob/2.4.1/examples/json-jackson/src/main/java/org/glassfish/jersey/examples/jackson/MyObjectMapperProvider.java

ich auf diese Weise verwendet zu tun, und das löste mein prob Lems und Jackson Annotationen werden vom Jackson-Provider korrekt gescannt.


Off topic Ich schlage vor, Sie diese Syntax in Ihrem Bean verwenden Karte zu initialisieren:

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

Leider jeder macht dies viel schwieriger, als es sein muss. Das Jersey-Team entschied sich in ihrer Weisheit, Jackson 1.9 zu integrieren, damit ihre Sachen Ihnen nicht helfen.

Aber es war ziemlich einfach für mich.Genau dies tun:

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

Jetzt RID OF THIS GET:

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

Dann in Ihre web.xml ändern diese Zeile:

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

sein:

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

Das sollte es tun.

+0

Dank rüttelt. Ich habe es versucht, aber es scheint keinen Unterschied gemacht zu haben. Es ignoriert weiterhin die fasterxml-Annotationen und erzeugt die falsche JSON-Ausgabe. Gibt es noch etwas, das ich ändern muss? – jcstockdale

+0

Können Sie mir die spezifischen Anmerkungen geben, die Sie verwenden möchten? Ich werde einen Testfall einrichten. –

+0

Meinst du die JsonIgnore Annotation? Versuchen Sie es auf den Getter und/oder den Setter nicht das private Feld. –

11

EDIT: Verwenden Sie das alte Konzept nicht unter wie es Fehler erzeugt (zumindest mit mit Android-Gerät, um weitere Informationen zu EDIT2 sehen). Nach meinen Tests scheint Jersey v2.6 das Problem mit dem @Provide zu beheben, welcher Ansatz nicht funktionierte. Ich konnte es mit diesem einfachen Anbieter arbeiten:

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

Also bitte nicht meinen Hack von unten verwenden.


OLD ANSATZ

Mit

@Provider 
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> 

war nicht für mich arbeiten (Jersey 2.4 & Jackson 2.3) und vielleicht ist dies auf eine in der Jackson-Anbieter berichtet Fehler in der Code, wo die ContextResolver in JacksonJsonProvider.java (2.3rc1) registriert werden sollte:

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

Aber zumindest kann ich nicht auf zugreifen, also weiß ich nicht, worum es sich bei diesem Fehler handelt.

Aber ich fand eine Abhilfe (ein Hack, wenn Sie so wollen). Nur verlängern JacksonJsonProvider mit der richtigen Anmerkung und senden Sie Ihre ObjectMapper wie folgt aus:

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

keine Notwendigkeit, etwas zu tun, es selbst registrieren, wird (mit Protokoll überprüfen, wird es das erste Mal, wenn Sie einen json Rest Dienst zugreifen registrieren). Das funktioniert jetzt für mich, nicht elegant, aber ich habe aufgegeben.

EDIT: mit Vorsicht verwenden - Im einen Fehler vielleicht zu diesem Hack im Zusammenhang erleben: Android Volley eine POST/PUT-Anfrage mit einer Anfrage Körper kann nicht senden, immer 400 bekommen von Rahmen, ich werde untersuchen und meine Erkenntnisse berichten.

EDIT2: Dieser Hack war in der Tat für eine generische 400 verantwortlich, wann immer eine Android-App mit Volley und OKHTTP Client versucht, eine POST oder PUT Anfrage zu tun, so dass nicht verwenden - in meinem Test Jersey 2.6 scheint dies zu beheben, so dass Sie @Provide Ansatz

+0

Was ist die Klasse 'ObjectMapperManager', die Sie verwenden? Ist es dein eigenes? –

+0

nur eine benutzerdefinierte Fabrikklasse, Sie können es einfach ersetzen mit zB. 'neue ObjectMapper()' – for3st

+0

nur eine benutzerdefinierte Factory-Klasse, können Sie es einfach ersetzen mit zB. 'new ObjectMapper()' – for3st

5

Mit Jersey 2.13 verwenden, können Sie die @Provider zwingen, dasselbe zu verwenden ObjectMapper nur ein ObjectMapper schaffen soll:

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

ich dies in meinem ObjectMapper-@Autowire verwenden, um generieren Sie json-schema Dokumente im laufenden Betrieb.

+0

Können Sie eine Lösung verwenden, ohne alle anderen Klassen zu infizieren? –

+0

@ AndréSchild: fertig. – Nthalk

-1

hinzufügen auf jeder Modellklasse wie diese

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

es für mich gearbeitet hat, aber ich verwendet habe codehus jackson

+0

finden Sie unter http://stackoverflow.com/help/how-to-answer – Vikrant

Verwandte Themen