【问题标题】:How to serialize Java primitives using Jersey REST如何使用 Jersey REST 序列化 Java 原语
【发布时间】:2011-02-07 10:38:02
【问题描述】:

在我的应用程序中,我使用 Jersey REST 序列化复杂对象。这工作得很好。但是有一些方法可以简单地返回一个 int 或 boolean。

Jersey 无法处理原始类型(据我所知),可能是因为它们没有注释,而 Jersey 没有为它们提供默认注释。我通过创建像 RestBoolean 或 RestInteger 这样的复杂类型来解决这个问题,它们只保存一个 int 或 boolean 值并具有适当的注释。

难道没有比编写这些容器对象更简单的方法吗?

【问题讨论】:

  • JAX-RS/Jersey 不支持原始类型的序列化,甚至不支持 Integer、Boolean 等包装类型。AFAIK,您采取的方法似乎是唯一的方法。

标签: java serialization rest jersey primitive


【解决方案1】:

我刚刚发现用 Jersey 返回原始类型是有问题的。我决定改为返回 String 。也许这不干净,但我不认为它太脏。 Java客户端大部分时间都是由服务器的同一作者编写的,可以将这样的字符串返回值包装起来,并将其转换回int。用其他语言编写的客户端必须以任何方式了解返回类型。

定义 RestInteger,RestBoolean 可能是另一种选择,但它更麻烦,而且我认为它的优势太小,没有吸引力。

或者我在这里遗漏了一些重要的东西?

【讨论】:

  • 这绝对不应该这么繁琐,我认为应该包含在球衣类中。我在周日晚上 10 点得出了同样的结论,我将只处理该死的字符串哈。
【解决方案2】:

我今天遇到了同样的问题,直到找到一个非常合适的解决方案才放弃。我无法从 1.1.5 更新球衣库,它是一个旧系统。我的休息服务返回一个列表,他们应该遵循这些规则。

  1. 空列表呈现为 [](几乎不可能)
  2. 一个元素列表呈现为 [](困难但仅映射配置)
  3. 许多元素列表呈现为 [](简单)

从简单到不可能。

3) 今天没有正常的 JSON 映射

2) 像下面这样注册 JAXBContextResolver

@Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
    private final JAXBContext context;
    private final Set<Class<?>> types;
    private Class<?>[] ctypes = { Pojo.class }; //your pojo class
    public JAXBContextResolver() throws Exception {
        this.types = new HashSet<Class<?>>(Arrays.asList(ctypes));
        this.context = new JSONJAXBContext(JSONConfiguration.mapped()
                .rootUnwrapping(true)
                .arrays("propertyName") //that should rendered as JSONArray even if the List only contain one element but doesn't handle the empty Collection case
                .build()
                , ctypes);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return (types.contains(objectType)) ? context : null;
    }
}

1) 以下方法仅适用于 Collections$EmptyList 类。愿您找到一种方法使其适用于所有为空的集合。可以这样处理 EmptyList 的代码。

@Provider
@Produces(value={MediaType.APPLICATION_JSON})
public class EmptyListWriter implements MessageBodyWriter<AbstractList> {

    private static final String EMPTY_JSON_ARRAY = "[]";

    @Override
    public long getSize(AbstractList list, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) {
        return EMPTY_JSON_ARRAY.length();
    }

    @Override
    public boolean isWriteable(Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) {
        return clazz.getName().equals("java.util.Collections$EmptyList");
    }

    @Override
    public void writeTo(AbstractList list, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType, 
            MultivaluedMap<String, Object> headers, OutputStream outputStream) throws IOException, WebApplicationException {
        if (list.isEmpty())
            outputStream.write(EMPTY_JSON_ARRAY.getBytes());            
    }
}

【讨论】:

    【解决方案3】:

    看看Genson。它在类似的问题上帮助了我很多。使用 Genson,您可以使用 int、boolean、lists 等泛型...这是一个简单的示例。

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getMagicList() {
        List<Object> objList = new ArrayList<>();
        stringList.add("Random String");
        stringList.add(121); //int
        stringList.add(1.22); //double
        stringList.add(false); //bolean
    
        return Response.status(Status.OK).entity(objList).build();
    }
    

    这将产生一个有效的 JSON 女巫,可以像这样非常简单地检索:

        Client client = Client.create();
        WebResource webResource = client.resource("...path to resource...");
        List objList = webResource.accept(MediaType.APPLICATION_JSON).get(ArrayList.class);
        for (Object obj : objList) {
            System.out.println(obj.getClass());
        }
    

    您将看到 Genson 还将帮助您在客户端解码 JSON 并为每个输出正确的类。

    【讨论】:

      【解决方案4】:

      您是在编写服务还是客户端?在服务端,您只需编写一个MessageBodyWriter 来将数据流序列化为您的类型的Java 对象。在我的用例中,我将输出写入 JSON 或 XML 的服务,在 XML 的情况下,我只需在我的类的顶部添加一个 JAXB 注释就完成了。

      您看过泽西岛用户指南吗?

      3.6. Adding support for new representations

      【讨论】:

        【解决方案5】:

        告诉 Jersey 生成正确的 JSON 文档(自然 json)。我对 rest 应用程序和 JAXBContext 解析器使用相同的类,发现它是最干净的封装。

        更好的程序员可以实现帮助器来迭代 .class 文件并通过识别 @Annotation 标记自动列出适当的类。我不知道如何在自己的源代码中运行时。

        这两个链接有助于研究这个额外的 Java 行话。我不知道为什么没有 Jersey 参数可以让一切都开箱即用。

        WEB-INF/web.xml (sn-p):

        <servlet>
          <servlet-name>RESTServlet</servlet-name>
          <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
          <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.myapp.rest.RESTApplication</param-value>
          </init-param>
        </servlet>
        <servlet-mapping>
          <servlet-name>RESTServlet</servlet-name>
          <url-pattern>/servlet/rest/*</url-pattern>
        </servlet-mapping>
        

        com.myapp.rest.RESTApplication.java

        package com.myapp.rest;
        
        import java.util.*;
        import javax.ws.rs.core.Application;
        import javax.ws.rs.ext.ContextResolver;
        import javax.xml.bind.JAXBContext;
        import javax.xml.bind.JAXBException;
        import com.sun.jersey.api.json.JSONConfiguration;
        import com.sun.jersey.api.json.JSONJAXBContext;
        
        public class RESTApplication extends Application implements ContextResolver<JAXBContext> {
            private JAXBContext context;
            private Class<?>[] types;
        
            public RESTApplication() throws JAXBException {
                // list JAXB bean types to be used for REST serialization
                types = new Class[] {
                    com.myapp.rest.MyBean1.class, 
                    com.myapp.rest.MyBean2.class, 
                };
                context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
            }
        
            @Override
            public Set<Class<?>> getClasses() {
                // list JAXB resource/provider/resolver classes
                Set<Class<?>> classes = new HashSet<Class<?>>();
                //for(Class<?> type : types)
                //    classes.add(type);
                classes.add(MyBeansResource.class);
                classes.add(this.getClass()); // used as a ContextResolver class
                return classes;
            }
        
            @Override
            public JAXBContext getContext(Class<?> objectType) {
                // this is called each time when rest path was called by remote client
                for (Class<?> type : types) {
                    if (type==objectType)
                        return context;
                }
                return null;
            }
        }
        

        类 MyBean1,MyBean2 是普通的 java 对象,而 MyBeansResource 类是具有 @Path 休息功能的类。他们没有什么特别之处,除了这里和那里的标准 jaxp @Annotations。在这个 java 行话 JSON 文档之后有

        • 零或单元素列表数组始终写为 json 数组([] 字段)
        • 原始整数和布尔字段被写为 json 基元(不带引号)

        我使用如下环境

        • Sun Java JDK1.6.x
        • Apache Tomcat 6.x
        • Jersey v1.14 库 (jersey-archive-1.14.zip)
        • webapps/myapp/WEB-INF/lib 文件夹有 asm-3.3.1.jar, jackson-core-asl.jar, jersey-client.jar, jersey-core.jar, jersey-json.jar, jersey- server.jar、jersey-servlet.jar 库
        • 如果您使用 infomas-asl 发现工具,请添加可选的 annotation-detector.jar

        jersey-archive.zip 有较旧的 asm-3.1.jar 文件,可能工作正常,但 chapter_deps.html 链接到较新的文件。请参阅顶部的链接列表。

        编辑 我找到了一个出色的(快速、轻量级、仅 15KB)的注释发现工具。请参阅这篇文章,了解我如何在运行时自动发现类型,并且不再需要在每次添加新的 java(jaxb) bean 时编辑 RESTApplication。

        https://github.com/rmuller/infomas-asl/issues/7

        【讨论】:

          【解决方案6】:

          实际上,最好的办法是编写一个自定义的 ContextResolver Provider,如下所示,它使用 JSON 的 natural 构建。

             @Provider
             public class YourContextResolver implements ContextResolver<JAXBContext> {
          
              private JAXBContext context;
              private Class<?>[] types = { YourSpecialBean.class };
          
              public YourContextResolver() throws Exception {
                  this.context = new JSONJAXBContext(
                          JSONConfiguration.natural().build(), types);
              }
          
              public JAXBContext getContext(Class<?> objectType) {
                  for (int i = 0; i < this.types.length; i++)
                      if (this.types[i].equals(objectType)) return context;
          
                  return null;
              }
          }
          

          这里唯一需要注意的是 Class[] 中的 YourSpecialBean.class。这定义了该提供程序将自然解析的类类型数组。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-06
            • 1970-01-01
            • 2012-06-26
            • 2015-09-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多