通过GenericEntity <list <t>&gt; </list <t>在RESTful Response对象中使用Java-generics模板类型

时间:2013-03-14 11:44:45

标签: java rest generics jersey jax-rs

我有一个通用的JAX-RS资源类,我已经定义了一个通用的findAll方法

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

和用户类:

public class User extends GenericModel {
    ...
}

以下是示例子类定义:

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

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

我得到以下异常:

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.

如果我用定义的类替换T,例如像User那样:

GenericEntity<List<User>>(list)

然后它运作正常。

有关如何使其与通用T一起使用的任何想法吗?

4 个答案:

答案 0 :(得分:11)

编译源代码后,由行创建的(匿名)类:

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

使用类型变量来引用其父变量。由于类型变量在运行时没有值,因此不能使用这样的泛型。您被迫从调用站点传递所谓的类型令牌。这是一个需要从findAll()调用者传递令牌的示例,但您可以在构造函数中需要一个令牌并将其保存在实例变量中:

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

来电者会发送一个像

这样的标记
new GenericEntity<List<User>>() {}

如果您只使用非参数化子类,findAll()可能会利用反射来构建令牌(未经测试,希望您明白这一点):

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

您必须实现getType()才能返回所需的类型。它将是ParameterizedType的子类,能够表示类型List<DAO<User>>

答案 1 :(得分:9)

除了答案之外,还要在客户端上阅读Response对象:

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

答案 2 :(得分:2)

响应对象返回给客户端以响应发送到服务器的请求。 Response类具有Response.ResponseBuilder内部类,该内部类将每个属性集收集到其Response.ResponseBuilder类型的字段中。 Response.ResponseBuilder应用Builder设计模式来构造响应对象。

build()方法负责连接在构建过程中形成的Response.ResponseBuilder对象链。 E.g。

Response.status(200);   

状态方法在分配STATUS后返回Response.ResponseBuilder

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

实体对象分配类型的实体(返回的有效负载)  Response.ResponseBuilder并被分配给Response的实例变量。之后,status还会分配状态并返回Response.ResponseBuilder实例。构建器在build()方法调用时连接它们。

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

最后,构建方法构造完成(包含属性)Response。

现在出现了 GenericEntity 对象的问题。它表示泛型类型T的响应实体。

For Instance,

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

obj 是上面给出的List类型。实体方法接受通用的对象类型。如果需要特定类型,你必须将它作为GenericEntity类型的参数传递,在运行时它将被转换为特定类型的对象。

实际使用

希望我的Jersey Framework能够将我的Java模型中的List对象的JSON作为Response对象的一部分回复给客户端。

因此,  新的GenericEntity&gt; ---将对象/有效负载转换为List类型  新的GenericEntity ---运行时类型变为String

Webservice Side上的资源

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

    } 

<强>输出  响应发送回客户端。 [   “香蕉”,   “苹果” ]

如何阅读响应实体

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

答案 3 :(得分:1)

为了解决这个问题,我做了一个返回GenericEntity然后从我的泛型类中调用它的方法,如下所示:

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

}