8

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

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

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

4

6 回答 6

4

看看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 并为每个输出正确的类。

于 2013-02-03T10:00:59.397 回答
3

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

你看过泽西用户指南吗?

3.6. 添加对新表示的支持

于 2010-04-29T03:12:45.237 回答
2

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

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

这两个链接有助于研究这个额外的 Java 术语。我不知道为什么没有 Jersey 参数可以让所有工作开箱即用。

WEB-INF/web.xml(片段):

<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 jargon 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

于 2012-09-27T14:49:59.753 回答
2

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

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

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

于 2013-10-07T17:09:32.490 回答
2

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

   @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。这定义了该提供程序将自然解析的类类型数组。

于 2010-05-20T21:21:27.810 回答
2

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

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

从容易到不可能。

3)今天没有正常的JSON Mapping

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());            
    }
}
于 2016-01-08T19:52:21.493 回答