wake-up-neo.com

Verwenden des Java-Generics-Vorlagentyps in RESTful Response-Objekt über GenericEntity <List <T >>

Ich habe eine generische JAX-RS-Ressourcenklasse und eine generische findAll-Methode definiert

public abstract class GenericDataResource<T extends GenericModel> {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response findAll() {
        Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
        List<T> list = query.getResultList();
        return Response.ok(new GenericEntity<List<T>>(list) {}).build();
    }
}

und Benutzerklasse:

public class User extends GenericModel {
    ...
}

Und hier ist die Definition einer Beispielsubklasse:

@Path("users")
public class UserResource extends GenericDataResource<User> {

    public UserResource() {
        super(User.class);
    }
}

Ich bekomme unter Ausnahme:

com.Sun.jersey.api.MessageException: A message body writer for Java class 
Java.util.Vector, and Java type Java.util.List<T>, 
and MIME media type application/json was not found exception.

Wenn ich T durch eine definierte Klasse wie User ersetze, wie folgt:

GenericEntity<List<User>>(list)

dann funktioniert es gut.

Irgendeine Idee, wie ich es mit generischem T arbeiten lassen kann?

11
Emin

Sobald der Quellcode kompiliert ist, wird die (anonyme) Klasse, die von der Zeile erstellt wird:

new GenericEntity<List<T>>(list) {}

verwendet eine Typvariable, um auf das übergeordnete Element zu verweisen. Da Typvariablen zur Laufzeit keinen Wert haben, können Sie keine Generika wie diese verwenden. Sie müssen ein sogenanntes type token von der aufrufenden Site übergeben. Dies ist ein Beispiel, bei dem das Token vom Aufrufer von findAll() übergeben werden muss. Möglicherweise wird jedoch ein Token im Konstruktor benötigt und in einer Instanzvariablen gespeichert:

public abstract class GenericDataResource<T extends GenericModel> {
  public Response findAll(GenericEntity<List<T>> token) {
    Query query = em.createNamedQuery(modelClass.getSimpleName() + ".findAll");
    List<T> list = query.getResultList();
    return Response.ok(token).build();
  }
}

Der Anrufer sendet ein Token wie

new GenericEntity<List<User>>() {}

Wenn Sie nur nicht parametrisierte Unterklassen verwenden, kann findAll() die Reflexion nutzen, um das Token zu erstellen (ungeprüft, ich hoffe, Sie bekommen die Idee)

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response findAll() {
  Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
  List<T> list = query.getResultList();
  return Response.ok(new GenericEntity(list, getType())).build();
}

Sie müssen getType() implementieren, um den gewünschten Typ zurückzugeben. Es wird eine Unterklasse von ParameterizedType sein, die den Typ List<DAO<User>> angeben kann.

10
Raffaele

Lesen Sie zusätzlich zu den Antworten das Response-Objekt auf dem Client zurück

List<MyObject> list = response.readEntity(new GenericType<List<MyObject>>(){}));
8
jediz

Response object wird als Antwort auf eine an einen Server gesendete Anforderung an den Client zurückgegeben. Die Response-Klasse hat eine innere Response.ResponseBuilder-Klasse, die alle Eigenschaften erfasst, die auf ihr Feld vom Typ Response.ResponseBuilder gesetzt sind. Response.ResponseBuilder wendet das Builder-Entwurfsmuster an, um das Antwortobjekt zu erstellen.

Die build () -Methode ist dafür verantwortlich, die Kette von Response.ResponseBuilder-Objekten zu verbinden, die während des Builds gebildet werden. Z.B.

Response.status(200);   

statusmethode gibt Response.ResponseBuilder zurück, nachdem STATUS zugewiesen wurde

 Response.status(200).entity( AnyObj );

entitätsobjekt weist die Entität (die zurückgegebene Nutzlast) des Typs Response.ResponseBuilder zu und wird der Instanzvariable von Response zugewiesen. Danach weist status auch den Status zu und gibt die Response.ResponseBuilder-Instanz zurück. Der Builder verbindet sie zum Zeitpunkt des Aufrufs der build () - Methode.

Response.status(200).entity( obj ).build(); 

Schließlich erstellt die Build-Methode eine vollständige Antwort (mit festgelegten Eigenschaften).

Nun kommt die Frage nach GenericEntity object. Stellt eine Antworteinheit eines generischen Typs T dar.

Zum Beispiel,

GenericEntity> obj = new GenericEntity> (lst) {}; Response.status (200) .entity (obj) .build ();

obj ist der Typ der Liste wie oben angegeben. Entitätsmethode akzeptiert Object type, der generisch ist. Falls ein bestimmter Typ erforderlich ist, müssen Sie ihn als Argument des Typs "GenericEntity" übergeben, durch den er zur Laufzeit in ein Objekt des Typs "Specific" umgewandelt wird.

Praktischer Nutzen

Wollte mein Jersey Framework, um JSON des Array-Typs, der List-Objekt in meinem Java-Modell war, als Teil des Response-Objekts an den Client zu beantworten.

Daher wandelt new GenericEntity> --- das Objekt/die Payload in List-Typ um, und der neue GenericEntity --- Lauftyp wird String


Ressource auf der Webservice-Seite

public Response findAllFruits(@QueryParam("frtID") String frtID ) {

         List<String> lst = new ArrayList<String>();
         lst.add("Banana");
         lst.add("Apple");

         GenericEntity<List<String>> obj = new GenericEntity<List<String>>(lst) {};


          return Response.status(200).entity( obj ).build(); 

    } 

Ausgabe Antwort an Client zurückgesendet. ["Banane", "Apfel"]


Wie liest man das Antwortobjekt?

List<CustomType> myObj= response.readEntity(new GenericType<List<CustomType>>() {});
2
Yergalem

Um das Problem zu lösen, wurde eine Methode erstellt, um eine GenericEntity zurückzugeben und dann aus meiner generischen Klasse aufzurufen.

@XmlRootElement
public abstract class AbstractRest<T extends IEntity> {

    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}


public abstract class SmartWebServiceNEW<R extends AbstractRest<T>> {

    @GET
    @Produces({MediaType.APPLICATION_XML})
    public Response findAll() {
        List<T> lista = getDelegate().findAll();
        if (lista == null || lista.isEmpty()) {
            return Response.status(Response.Status.NO_CONTENT).build();
        }
        List<R> retorno = new ArrayList<R>();
        for (T it : lista) {
            retorno.add(toRest(it));
        }
        GenericEntity entity = listToGenericEntity(retorno);
        return Response.ok(entity).build();
    }

    protected abstract GenericEntity listToGenericEntity(List<R> restList);   
}

@Path("/something")
@RequestScoped
public class MyEntityResource extends SmartWebServiceNEW<MyEntityExtendingAbstractRest> {

 @Override
    protected GenericEntity listToGenericEntity(List<MyEntityExtendingAbstractRest> restList) {
        return new GenericEntity<List<MyEntityExtendingAbstractRest>>(restList) {
        };
    }

}
1
cristianchiess