2012-06-01 11 views
13

Ich benutze Jersey, um JAX-RS REST-Stil-Dienste zusammen mit Jackson 2.0.2 für die JSON-Zuordnung zu implementieren. Einer dieser REST-Services liefert ein List<EntityA> (wir es indexA nennen), wo EntityA andere List<EntityB> enthält, während ein anderer Dienst gibt nur ein List<EntityB> (lass es indexB nennen):Anwenden von verschiedenen Jackson-Filter für verschiedene Jersey REST Serviceanrufe

@Entity 
@JsonAutoDetect 
public class EntityA { 
    @Id 
    private String id; 

    @OneToMany 
    private List<EntityB> b; 

    ... 
} 

@Entity 
@JsonAutoDetect 
@JsonFilter("bFilter") 
public class EntityB { 
    @Id 
    private String id; 

    private String some; 
    private String other; 
    private String attributes; 

    ... 
} 

@Path("/a") 
public class AResource { 

    @GET 
    @Path("/") 
    public List<EntityA> indexA() { 
    ... 
    } 
} 

@Path("/b") 
public class BResource { 

    @GET 
    @Path("/") 
    public List<EntityB> indexB() { 
    ... 
    } 
} 

Was Ich mag würde zu erreichen ist anzuwenden ein Jackson-Filter auf den Aufruf indexA, so dass nicht alle Attribute der untergeordneten Elemente EntityB serialisiert sind. OTOH, indexB sollte EntityB in seiner Vollständigkeit zurückgeben.

Ich kenne die Existenz einer ContextResolver<ObjectMapper>, die ich bereits für andere Zwecke verwende. Leider scheint es für die ContextResolver unmöglich zu sein, beide Serviceaufrufe zu unterscheiden, da der Class, der an ContextResolver.getContext(Class) geliefert wird, in beiden Fällen ArrayList ist (und dank Typenlöschung kann ich die generischen Typparameter nicht herausfinden).

Gibt es irgendwelche Hooks, die bei der Konfiguration eines ObjectMapper/FilterProvider je nach Entitätstyp, der zugeordnet wird, besser geeignet sind?

Ich könnte den in How to return a partial JSON response using Java? vorgeschlagenen Ansatz verwenden: Manuelles Zuordnen zu einem String, aber das tötet die ganze Schönheit einer deklarativen Annotation-basierten Ansatz, also möchte ich dies vermeiden.

Antwort

27

Ich war in der gleichen Situation, nach Tonnen von Forschung, ich fand es heraus, die Lösung ist @JsonView und Spring, die eine ObjectMapper in den JSON Writer injizieren können, ohne die Schönheit von Jersey zu töten.

Ich arbeite an einer Reihe von REST APIs, ich möchte eine Liste der Instanzen von SystemObject und das Detail eine spezifische Instanz von SystemObject, genau wie Sie Ich möchte nur sehr begrenzt Anzahl der Eigenschaften der einzelnen Instanzen in der Liste und einige zusätzliche Eigenschaften im Detail, ich definiere nur Ansichten für sie und Annotation in der Klasse SystemObject hinzufügen. aber standardmäßig werden alle Eigenschaften ohne @JsonView Annotation an den JSON ausgegeben, aber es gibt eine Konfiguration item(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION), die ich verwenden kann, um sie auszuschließen.

Das Problem ist, dass ich es auf True setzen muss, um meine Bedürfnisse zu erfüllen. aber ich kann nicht den ObjectMapper ändern, der die Magie macht, das Objekt in JSON zu konvertieren, durch das Lesen der 3 Artikel unten, kam ich auf die Idee, dass der einzige Weg, den ich tun kann, ist ein Modified ObjectMapper nach Jersey zu injizieren. Jetzt habe ich was ich will.

Es ist wie wenn Sie mehrere Ansichten für eine Datenbanktabelle erstellen.

Diese drei Links werden Sie auf unterschiedliche Weise helfen:

How to create a ObjectMapperProvider which can be used by Spring to inject

Jersey, Jackson, Spring and JSON

Jersey + Spring integration example

REST-Ressource:

package com.john.rest.resource; 

import java.util.ArrayList; 
import java.util.List; 

import javax.ws.rs.GET; 
import javax.ws.rs.HeaderParam; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.WebApplicationException; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.core.Request; 
import javax.ws.rs.core.UriInfo; 

import org.codehaus.jackson.map.annotate.JsonView; 
import org.springframework.stereotype.Component; 

import com.midtronics.esp.common.EspException; 
import com.midtronics.esp.common.SystemObject; 
import com.midtronics.esp.mobile.model.SystemObjectView; 
import com.midtronics.esp.model.accesscontrol.AccessControlBean; 
import com.midtronics.esp.model.site.SiteBean; 

@Component 
@Path("/hierarchy") 
public class Hierarchy { 

    // Allows to insert contextual objects into the class, 
    // e.g. ServletContext, Request, Response, UriInfo 
    @Context 
    UriInfo uriInfo; 

    @Context 
    Request request; 

    // Return the list of sites 
    @GET 
    @Path("sites") 
    @Produces(MediaType.APPLICATION_JSON) 
    @JsonView({SystemObjectView.ObjectList.class}) 
    public List<SystemObject> listSite(
      @HeaderParam("userId") String userId, 
      @HeaderParam("password") String password) { 
     ArrayList<SystemObject> sites= new ArrayList<SystemObject>(); 

     try{ 
      if(!AccessControlBean.CheckUser(userId, password)){ 
       throw new WebApplicationException(401); 
      } 
      SystemObject.GetSiteListByPage(sites, 2, 3); 

      return sites; 
     } catch(EspException e){ 
      throw new WebApplicationException(401); 
     } catch (Exception e) { 
      throw new WebApplicationException(500); 
     } 
    } 

    // Return the number of sites 
    @GET 
    @Path("sites/total") 
    @Produces(MediaType.TEXT_PLAIN) 
    public String getSiteNumber(@HeaderParam("userId") String userId, 
      @HeaderParam("password") String password) { 
     try{ 
      return Integer.toString(SiteBean.GetSiteTotal()); 
     } catch(EspException e){ 
      throw new WebApplicationException(401); 
     } catch (Exception e) { 
      throw new WebApplicationException(500); 
     } 
    } 

} 

REST-Modell:

package com.john.rest.model; 

import java.io.Serializable; 
import java.util.ArrayList; 

import javax.xml.bind.annotation.XmlRootElement; 

import org.codehaus.jackson.annotate.JsonIgnore; 
import org.codehaus.jackson.annotate.JsonProperty; 
import org.codehaus.jackson.map.annotate.JsonView; 

import com.midtronics.esp.mobile.model.SystemObjectView; 
import com.midtronics.esp.model.common.ICommonDAO; 

@XmlRootElement 
public class SystemObject implements Serializable 
{ 
    private static final long serialVersionUID = 3989499187492868996L; 

    @JsonProperty("id") 
    @JsonView({SystemObjectView.ObjectList.class, SystemObjectView.ObjectDetail.class}) 
    protected String objectID = ""; 

    @JsonProperty("parentId") 
    protected String parentID = ""; 

    @JsonProperty("name") 
    @JsonView({SystemObjectView.ObjectList.class, SystemObjectView.ObjectDetail.class}) 
    protected String objectName = ""; 

    //getters... 
    //setters... 

} 

REST Modellansicht:

package com.john.rest.model; 

public class SystemObjectView { 
    public static class ObjectList { }; 

    public static class ObjectDetail extends ObjectList { } 
} 
+3

Wenn ich auf diese 20-mal abstimmen könnte, würde ich. Bis ich das gelesen habe, hatte ich keine Ahnung, dass Sie JsonView auf die Jersey-Ressourcenmethode anwenden können, um die Ansicht auszuwählen, mit der serialisiert werden soll. Wenn das in den Dokumenten beschrieben wird, habe ich es komplett vermisst. BTW - funktioniert ohne Spring (ich benutze Felix). Vielen Dank! – sfitts

+0

Ich glaube nicht, dass Sie das in der Dokumentation verpasst haben. Ich habe lange nach einem guten Weg gesucht, dies zu tun, und habe nie einen gefunden, bis ich diesen Beitrag gesehen habe. –

+0

Dank berichtet, Projekt README enthält jetzt kurze Beschreibung der Anmerkungen, die für JAX-RS Endpunkte - https://github.com/FasterXML/jackson-jaxrs-providers - und hoffentlich andere Container (Spring, Restlet) gelten habe/etwas ähnliches hinzufügen. – StaxMan

Verwandte Themen