Jaxb JAX-RS:如何在返回响应对象时自动序列化集合?

Jaxb JAX-RS:如何在返回响应对象时自动序列化集合?,jaxb,jersey,jax-rs,Jaxb,Jersey,Jax Rs,我有一个JAXB注释的employee类: @XmlRootElement(name=“employee”) 公营雇员{ 私有整数id; 私有字符串名称; ... @xmlement(name=“id”) 公共int getId(){ 返回此.id; } …//name、equals、hashCode和toString的setter和getter } 和一个JAX-RS资源对象(我使用的是Jersey 1.12) @GET @使用({MediaType.APPLICATION\uxml,Med

我有一个JAXB注释的employee类:

@XmlRootElement(name=“employee”)
公营雇员{
私有整数id;
私有字符串名称;
...
@xmlement(name=“id”)
公共int getId(){
返回此.id;
}
…//name、equals、hashCode和toString的setter和getter
}
和一个JAX-RS资源对象(我使用的是Jersey 1.12)

@GET
@使用({MediaType.APPLICATION\uxml,MediaType.APPLICATION\ujson})
@产生({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@路径(“/”)
公开名单查找员工(
@QueryParam(“名称”)字符串名称,
@QueryParam(“页面”)字符串页码,
@QueryParam(“pageSize”)字符串(pageSize){
...
List employees=employeeService.findeemployees(…);
返回员工;
}
这个端点工作得很好。我明白了


2.
安娜
但是,如果我更改方法以返回
响应
对象,并将员工列表放入响应正文中,如下所示:

@GET
@使用({MediaType.APPLICATION\uxml,MediaType.APPLICATION\ujson})
@产生({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@路径(“/”)
公众对雇主的反应(
@QueryParam(“名称”)字符串名称,
@QueryParam(“页面”)字符串页码,
@QueryParam(“pageSize”)字符串(pageSize){
...
List employees=employeeService.findeemployees(…);
返回Response.ok().entity(employees.build();
}
由于以下异常,终结点导致HTTP 500:

javax.ws.rs.WebApplicationException:com.sun.jersey.api.MessageException:Java类Java.util.ArrayList、Java类Java.util.ArrayList和MIME媒体类型application/xml的消息体编写器未找到

在第一种情况下,JAX-RS显然已经安排了适当的消息编写器在返回集合时启动。当集合放置在实体体中时,这种情况不会发生,这似乎有点不一致。在返回响应时,我可以采取什么方法来实现列表的自动JAXB序列化

我知道我可以

  • 只需从资源方法返回列表
  • 创建一个单独的
    EmployeeList

但是我想知道是否有一种很好的方法可以使用
响应
对象并让列表序列化,而无需创建自己的包装类。

您可以将
列表
包装在
GenericEntity
的实例中,以保留类型信息:


我通过扩展默认的JacksonJsonProvider类,特别是方法writeTo,解决了这个问题

通过分析此类的源代码,我找到了通过反射实例化实际类型的块,因此我对源代码进行了如下修改:

public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) throws IOException {
    /* 27-Feb-2009, tatu: Where can we find desired encoding? Within
     *   HTTP headers?
     */
    ObjectMapper mapper = locateMapper(type, mediaType);
    JsonEncoding enc = findEncoding(mediaType, httpHeaders);
    JsonGenerator jg = mapper.getJsonFactory().createJsonGenerator(entityStream, enc);
    jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);

    // Want indentation?
    if (mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
        jg.useDefaultPrettyPrinter();
    }
    // 04-Mar-2010, tatu: How about type we were given? (if any)
    JavaType rootType = null;

    if (genericType != null && value != null) {
        /* 10-Jan-2011, tatu: as per [JACKSON-456], it's not safe to just force root
         *    type since it prevents polymorphic type serialization. Since we really
         *    just need this for generics, let's only use generic type if it's truly
         *    generic.
         */
        if (genericType.getClass() != Class.class) { // generic types are other impls of 'java.lang.reflect.Type'
            /* This is still not exactly right; should root type be further
             * specialized with 'value.getClass()'? Let's see how well this works before
             * trying to come up with more complete solution.
             */

            //**where the magic happens**
            //if the type to instantiate implements collection interface (List, Set and so on...)
            //Java applies Type erasure from Generic: e.g. List<BaseRealEstate> is seen as List<?> and so List<Object>, so Jackson cannot determine @JsonTypeInfo correctly
            //so, in this case we must determine at runtime the right object type to set
            if(Collection.class.isAssignableFrom(type))
            {
                Collection<?> converted = (Collection<?>) type.cast(value);
                Class<?> elementClass = Object.class;
                if(converted.size() > 0)
                    elementClass = converted.iterator().next().getClass();
                //Tell the mapper to create a collection of type passed as parameter (List, Set and so on..), containing objects determined at runtime with the previous instruction
                rootType = mapper.getTypeFactory().constructCollectionType((Class<? extends Collection<?>>)type, elementClass);
            }
            else
                rootType = mapper.getTypeFactory().constructType(genericType);
            /* 26-Feb-2011, tatu: To help with [JACKSON-518], we better recognize cases where
             *    type degenerates back into "Object.class" (as is the case with plain TypeVariable,
             *    for example), and not use that.
             */
            if (rootType.getRawClass() == Object.class) {
                rootType = null;
            }
        }
    }
    // [JACKSON-578]: Allow use of @JsonView in resource methods.
    Class<?> viewToUse = null;
    if (annotations != null && annotations.length > 0) {
        viewToUse = _findView(mapper, annotations);
    }
    if (viewToUse != null) {
        // TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
        ObjectWriter viewWriter = mapper.viewWriter(viewToUse);
        // [JACKSON-245] Allow automatic JSONP wrapping
        if (_jsonpFunctionName != null) {
            viewWriter.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
        } else if (rootType != null) {
            // TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
            mapper.typedWriter(rootType).withView(viewToUse).writeValue(jg, value);
        } else {
            viewWriter.writeValue(jg, value);
        }
    } else {
        // [JACKSON-245] Allow automatic JSONP wrapping
        if (_jsonpFunctionName != null) {
            mapper.writeValue(jg, new JSONPObject(this._jsonpFunctionName, value, rootType));
        } else if (rootType != null) {
            // TODO: change to use 'writerWithType' for 2.0 (1.9 could use, but let's defer)
            mapper.typedWriter(rootType).writeValue(jg, value);
        } else {
            mapper.writeValue(jg, value);
        }
    }
}
public void writeTo(对象值、类类型、类型genericType、注释[]注释、MediaType MediaType、多值Map HttpHeader、OutputStream entityStream)引发IOException{
/*2009年2月27日,塔图:我们在哪里可以找到想要的编码?在
*HTTP头?
*/
ObjectMapper mapper=locateMapper(类型,mediaType);
JsonEncoding enc=findEncoding(mediaType,httpHeaders);
JsonGenerator jg=mapper.getJsonFactory().createJsonGenerator(entityStream,enc);
禁用(jsonggenerator.Feature.AUTO\u CLOSE\u目标);
//想要缩进吗?
if(mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.INDENT_输出)){
jg.useDefaultPrettyPrinter();
}
//2010年3月4日,塔图:我们得到的类型如何?(如果有的话)
JavaType rootType=null;
if(genericType!=null&&value!=null){
/*2011年1月10日,塔图:根据[JACKSON-456],仅仅强制root是不安全的
*类型,因为它阻止多态类型序列化
*对于泛型,我们只需要使用泛型类型,如果它确实是
*通用的。
*/
if(genericType.getClass()!=Class.Class){//泛型类型是'java.lang.reflect.Type'的其他impl
/*这仍然不完全正确;根类型应该更进一步吗
*专用于“value.getClass()”?让我们看看之前的工作情况
*试图提出更完整的解决方案。
*/
//**魔法发生在哪里**
//如果要实例化的类型实现了集合接口(列表、集合等…)
//Java从泛型中应用类型擦除:例如,List被视为List和so List,因此Jackson无法正确确定@JsonTypeInfo
//因此,在这种情况下,我们必须在运行时确定要设置的正确对象类型
if(Collection.class.isAssignableFrom(type))
{
已转换的集合=(集合)类型.cast(值);
Class elementClass=Object.Class;
如果(已转换.size()>0)
elementClass=converted.iterator().next().getClass();
//告诉映射程序创建一个作为参数传递的类型集合(List、Set等…),其中包含在运行时使用上一条指令确定的对象
rootType=mapper.getTypeFactory().ConstructionCollectionType((类>)类型,elementClass);
}
其他的
rootType=mapper.getTypeFactory().constructType(genericType);
/*2011年2月26日,塔图:为了帮助[JACKSON-518],我们更好地识别
*类型退化回“Object.class”(与普通TypeVariable一样,
*例如),不要使用它。
*/
if(rootType.getRawClass()==Object.class){
rootType=null;
}
}
}
//[JACKSON-578]:允许在资源方法中使用@JsonView。
类viewToUse=null;
我
    @GET
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Path("/")
    public Response findEmployees(
        @QueryParam("name") String name, 
        @QueryParam("page") String pageNumber,
        @QueryParam("pageSize") String pageSize) {
        ...
        List<Employee> employees = employeeService.findEmployees(...);

        GenericEntity<List<Employee>> entity = new GenericEntity<List<Employee>>(Lists.newArrayList(employees))

        return Response.ok().entity(entity).build();
    }