简体   繁体   English

JAXRS / RestEasy中的@Produces集合

[英]@Produces collection in JAXRS / RestEasy

I found some strange behaviour that I cannot understand. 我发现了一些我无法理解的奇怪行为。

I have tested 4 similar examples: 我测试了4个类似的例子:

1 1

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return Response.ok(books).build();
}

2 2

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Book> produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return books;
}

3 3

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Book> produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return books;
}

4 4

@GET
@Produces(MediaType.APPLICATION_XML)
public Response produce() {
    List<Book> books = Arrays
            .asList(new Book[] { 
                    new Book("aaa", "AAA", "12345"), 
                    new Book("bbb", "BBB", "09876") 
                    });
    return Response.ok(books).build();
}

Everything works in #1, #2, #3 but 4th example throws: 一切都在#1,#2,#3中起作用,但第四个例子抛出:

Could not find MessageBodyWriter for response object of type: java.util.Arrays$ArrayList of media type: application/xml. 无法为类型的响应对象找到MessageBodyWriter:java.util.Arrays $ ArrayList of media type:application / xml。

I run it on Wildfly 9 and I wonder if it is related to RestEasy or JaxRS in general? 我在Wildfly 9上运行它,我想知道它是否与RestEasy或JaxRS有关? I know that I can fix it by wrapping collection in GenericEntity, but I don't understand this inconsistent behaviour. 我知道我可以通过在GenericEntity中包装集合来修复它,但我不理解这种不一致的行为。

The problem is the missing of type information. 问题是缺少类型信息。 This is required for JAXB, which handles the XML serialization. 这是处理XML序列化的JAXB所必需的。

1 and 2 works because Jackson is being used for JSON, and it generally doesn't need to know type information as it just introspects properties. 1和2的工作原理是因为Jackson被用于JSON,它通常不需要知道类型信息,因为它只是内省属性。

3 works because type information is known through the method return type. 3有效,因为通过方法返回类型已知类型信息。

4 doesn't work because there is no type information. 4不起作用,因为没有类型信息。 It's is erased by type erasure . 它被类型擦除所抹去 That's where GenericEntity comes to the rescue. 这就是GenericEntity拯救的地方。 It stores the type information. 它存储类型信息。

GenericEntity

Normally type erasure removes generic type information such that a Response instance that contains, eg, an entity of type List<String> appears to contain a raw List<?> at runtime. 通常,类型擦除会删除泛型类型信息,以便包含例如List<String>类型的实体的Response实例在运行时似乎包含原始List<?> When the generic type is required to select a suitable MessageBodyWriter , this class may be used to wrap the entity and capture its generic type. 当需要泛型类型来选择合适的MessageBodyWriter ,可以使用此类来包装实体并捕获其泛型类型。

swch, i create an example of MessageBodyWriter for Collections (Set, List etc) Also, some can analyze Annotation for xml root name, gzip, and cache... have fun! swch,我为集合创建了一个MessageBodyWriter示例(Set,List等)。另外,有些人可以分析xml根名称,gzip和缓存的注释......玩得开心!

import java.io.IOException;
import java.io.OutputStream;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;

@Provider
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class CollectionProvider
  implements MessageBodyWriter<Collection>
{
  static final byte[] COMMA = ",".getBytes();
  static final byte[] ARRAY_START = "[".getBytes();
  static final byte[] ARRAY_END = "]".getBytes();
  static final byte[] NULL = "null".getBytes();
  static final QName OBJECT = new QName(null, "object");

  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
  {
    if (!Collection.class.isAssignableFrom(type))
      return false;

    if (genericType == null || !(genericType instanceof ParameterizedType))
      return false;

    Type[] args = ((ParameterizedType) genericType).getActualTypeArguments();

    for (Type arg: args)
    {
      if (arg instanceof TypeVariable) // can't mashal Collection<T>
        return false;

      if (!(arg instanceof Class))
        return false;
    }

    String type = mediaType.getType().toLowerCase();
    String subtype = mediaType.getSubtype().toLowerCase();

    return type.equals("application") && 
      (subtype.startsWith("json") || subtype.startsWith("xml"));
  }

  @Override
  public long getSize(Collection list, Class<?> c, Type type, Annotation[] annotation, MediaType mediaType)
  {
    return -1;
  }

  @Override
  public void writeTo(Collection list, Class<?> c, Type type, Annotation[] annotation, MediaType mediaType,
                      MultivaluedMap<String, Object> multivaluedMap, OutputStream outputStream)
    throws IOException, WebApplicationException
  {
    try
    {
      boolean json = mediaType.getSubtype().toLowerCase().startsWith("json");

      if (list.isEmpty())
      {
        if(json)
        {
          outputStream.write(ARRAY_START);
          outputStream.write(ARRAY_END);
        }
        else
          outputStream.write("<list/>".getBytes());
      }
      else
      {
        Set<Class> classes = new HashSet<Class>();

        for (Type clazz: ((ParameterizedType) type).getActualTypeArguments())
          classes.add((Class) clazz);

        JAXBContext jc = JAXBContext.newInstance(classes.toArray(new Class[classes.size()]));
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);

        if(json)
        {
          m.setProperty("eclipselink.media-type", MediaType.APPLICATION_JSON);
          m.setProperty("eclipselink.json.include-root", false);
        }

        if(json)
          outputStream.write(ARRAY_START);
        else
          outputStream.write("<list>".getBytes());

        for (Iterator it = list.iterator(); it.hasNext();)
        {
          Object object = it.next();

          if(json)
          {
            if (object == null) // Allow nullabale value collections
              outputStream.write(NULL);
            else
              m.marshal(new JAXBElement(OBJECT, object.getClass(), object), outputStream);

            if (it.hasNext())
              outputStream.write(COMMA);
          }
          else if (object != null) // null in xml? xm...
            m.marshal(object, outputStream); // <-- requered XmlRoot annotation
        }

        if(json)
          outputStream.write(ARRAY_END);
        else
          outputStream.write("</list>".getBytes());
      }
    }
    catch (JAXBException e)
    {
      throw new IOException(e);
    }
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 错误 [org.jboss.resteasy.resteasy_jaxrs.i18n](默认任务 2)RESTEASY002010 - ERROR [org.jboss.resteasy.resteasy_jaxrs.i18n] (default task-2) RESTEASY002010 ClassNotFoundException:org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder - ClassNotFoundException: org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder @Path regexp中的Jaxrs(RESTEasy)多个子资源 - Jaxrs (RESTEasy) multiple sub-resources in @Path regexp 是否可以在WebLogic12c上使用Resteasy(或不使用Jersey-JAXRS实现)? - Resteasy (or not Jersey - JAXRS implementation) on WebLogic12c, is possible? 如何在Angular 5应用程序中使用jaxrs(resteasy)将Set或List映射到数组? - How to map Set or List to array using jaxrs (resteasy) in Angular 5 app? Keycloak 和 RESTEasy 客户端:ClassNotFoundException:org.jboss.resteasy.client.jaxrs.internal.proxy.ProxyBuilderImpl - Keycloak and RESTEasy client: ClassNotFoundException: org.jboss.resteasy.client.jaxrs.internal.proxy.ProxyBuilderImpl JaxRS + RestEasy - 如何创建自己的 @Context 注入字段? - JaxRS + RestEasy - How do you create your own @Context injected field? RESTEasy - 简单的字符串数组/集合编组 - RESTEasy - simple string array/collection marshalling 使用带有 TLS 的 cxf-spring-boot-starter-jaxrs WebClient 产生证书未知错误 - Using cxf-spring-boot-starter-jaxrs WebClient with TLS produces Certificate Unknown Error 有没有一种简单的方法可以使用RestEasy通过JSON传递对象集合? - Is there an easy way to pass a collection of objects via JSON using RestEasy?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM