如何使用 Jersey REST 序列化 Java 原语
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2629544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to serialize Java primitives using Jersey REST
提问by Olvagor
In my application I use Jersey REST to serialize complex objects. This works quite fine. But there are a few method which simply return an int or boolean.
在我的应用程序中,我使用 Jersey REST 来序列化复杂的对象。这工作得很好。但是有一些方法只返回一个 int 或 boolean。
Jersey can't handle primitive types (to my knowledge), probably because they're no annotated and Jersey has no default annotation for them. I worked around that by creating complex types like a RestBoolean or RestInteger, which simply hold an int or boolean value and have the appropriate annotations.
Jersey 无法处理原始类型(据我所知),可能是因为它们没有注释并且 Jersey 没有默认注释。我通过创建像 RestBoolean 或 RestInteger 这样的复杂类型来解决这个问题,它们只保存一个 int 或 boolean 值并具有适当的注释。
Isn't there an easier way than writing these container objects?
没有比编写这些容器对象更简单的方法吗?
回答by mvlupan
Have a look at Genson.It helped me a lot with a similar problem.With Genson you could use generics like int,boolean, lists and so on...Here is a quick example.
看看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();
}
This will produce a valid JSON witch can be retrieved very simple like this:
这将产生一个有效的 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());
}
You will see that Genson will help you decode the JSON on the client side also and output the correct class for each.
您将看到 Genson 还将帮助您在客户端解码 JSON,并为每个输出正确的类。
回答by Nick Klauer
Are you writing a service or a client? In the service-end of things, you would simply write a MessageBodyWriterto serialize a stream of data to a Java object for your types. In my use cases, the services I'm writing output to JSON or XML, and in XML's case, I just throw one JAXB annotation on the top of my classes and I'm done.
你是写服务还是客户端?在服务端,您只需编写MessageBodyWriter来将数据流序列化为您的类型的 Java 对象。在我的用例中,我将输出写入 JSON 或 XML 的服务,而在 XML 的情况下,我只是在我的类的顶部抛出一个 JAXB 注释,我就完成了。
Have you looked at the Jersey User guide regarding this?
你有没有看过关于这个的泽西用户指南?
回答by Alex Winston
Actually your best bet is to write a custom ContextResolver Provider like the following that uses naturalbuilding of JSON.
实际上,最好的办法是编写一个自定义的 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;
}
}
The only thing special here to notice is the YourSpecialBean.class in the Class[]. This defines an array of class types that this provider will resolve naturally.
这里唯一需要注意的是 Class[] 中的 YourSpecialBean.class。这定义了此提供程序将自然解析的类类型数组。
回答by Whome
Tell Jersey generate proper JSON documents (natural json). I use same class for rest app and JAXBContext resolver, found it the most clean encapsulation.
告诉 Jersey 生成正确的 JSON 文档(自然 json)。我对其余应用程序和 JAXBContext 解析器使用相同的类,发现它是最干净的封装。
Better programmer could implement helper to iterate .class files and list appropriate classes automatically by identifying @Annotation tags. I don't know how to do it runtime in an own source code.
更好的程序员可以实现 helper 来迭代 .class 文件并通过识别 @Annotation 标签自动列出适当的类。我不知道如何在自己的源代码中运行它。
These two links were helpful studying this extra java jargon. I don't know why there is no Jersey parameter to make all just work out of the box.
这两个链接有助于研究这个额外的 Java 术语。我不知道为什么没有 Jersey 参数让所有东西都开箱即用。
- http://jersey.java.net/nonav/documentation/latest/json.html
- http://jersey.java.net/nonav/documentation/latest/chapter_deps.html
- https://maven.java.net/content/repositories/releases/com/sun/jersey/jersey-archive/
- http://search.maven.org/remotecontent?filepath=asm/asm/3.3.1/asm-3.3.1.jar
- https://github.com/rmuller/infomas-asl/
- http://jersey.java.net/nonav/documentation/latest/json.html
- http://jersey.java.net/nonav/documentation/latest/chapter_deps.html
- https://maven.java.net/content/repositories/releases/com/sun/jersey/jersey-archive/
- http://search.maven.org/remotecontent?filepath=asm/asm/3.3.1/asm-3.3.1.jar
- https://github.com/rmuller/infomas-asl/
WEB-INF/web.xml (snippet):
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
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;
}
}
Classes MyBean1,MyBean2 are plain java objects and MyBeansResource class is the one with @Path rest functions. There is nothing special in them expect standard jaxp @Annotations here and there. After this java jargon JSON documents have
类 MyBean1、MyBean2 是普通的 Java 对象,而 MyBeansResource 类是具有 @Path 其余功能的类。除了标准的 jaxp @Annotations 之外,它们并没有什么特别之处。这个java行话JSON文件后有
- zero or single-element List arrays are always written as json array ([] field)
- primitive integers and boolean fields are written as json primitives (without quotations)
- 零或单元素列表数组始终写为 json 数组([] 字段)
- 原始整数和布尔字段写成 json 原语(不带引号)
I use the following environment
我使用以下环境
- Sun Java JDK1.6.x
- Apache Tomcat 6.x
- Jersey v1.14 libraries (jersey-archive-1.14.zip)
- webapps/myapp/WEB-INF/lib folder has asm-3.3.1.jar, Hymanson-core-asl.jar, jersey-client.jar, jersey-core.jar, jersey-json.jar, jersey-server.jar, jersey-servlet.jar libraries
- add optional annotation-detector.jar if you use infomas-asl discovery tool
- 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, Hymanson-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 had older asm-3.1.jar file, probably works fine but chapter_deps.html links to a newer file. See link list at the top.
jersey-archive.zip 有较旧的 asm-3.1.jar 文件,可能工作正常,但chapter_deps.html 链接到较新的文件。请参阅顶部的链接列表。
EditI found an excellent(fast, lightweight just 15KB) annotation discovery tool. See this post about how I autodiscover types at runtime and no longer need to edit RESTApplication each time new java(jaxb) bean is added.
编辑我发现了一个优秀的(快速、轻量级的只有 15KB)的注释发现工具。请参阅这篇关于我如何在运行时自动发现类型并且不再需要在每次添加新 java(jaxb) bean 时编辑 RESTApplication 的帖子。
回答by zakmck
I've just discovered that returning a primitive type with Jersey is problematic. I've decided to return String instead. Maybe this is not clean, but I don't think it's too dirty. The Java client, which is written by the same author of the server most of the times, can wrap such a string return value and convert it back to int. Clients written in other languages must be aware of return types any way.
我刚刚发现用 Jersey 返回原始类型是有问题的。我决定改为返回 String 。也许这不干净,但我不认为它太脏了。大多数时候由服务器的同一作者编写的Java客户端可以包装这样的字符串返回值并将其转换回int。用其他语言编写的客户端必须以任何方式了解返回类型。
Defining RestInteger, RestBoolean may be another option, however it's more cumbersome and I see too little advantage in it to be attractive.
定义 RestInteger、RestBoolean 可能是另一种选择,但它更麻烦,我认为它的优势太小而无法吸引人。
Or maybe am I missing something important here?
或者也许我在这里遗漏了一些重要的东西?
回答by Frank Ittermann
I had the same problem today and didn't give up until i found a really good suitable solution. I can not update the jersey library from 1.1.5 it is a Legacy System. My Rest Service returns a List and they should follow those rules.
我今天遇到了同样的问题,直到我找到了一个非常合适的解决方案才放弃。我无法从 1.1.5 更新球衣库,它是旧系统。我的休息服务返回一个列表,他们应该遵循这些规则。
- Empty Lists are rendered as [] (almost impossible)
- One Element Lists are rendered as [] (difficult but only mapping configuration)
- Many Element Lists are rendered as [] (easy)
- 空列表呈现为 [](几乎不可能)
- 一个元素列表呈现为 [](困难但只有映射配置)
- 许多元素列表呈现为 [](简单)
Start from easy to impossible.
从容易到不可能开始。
3) nothing today normal JSON Mapping
3)今天没有什么正常的JSON映射
2) Register JAXBContextResolver like the following
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) The following approach only works for Collections$EmptyList class. May you find a way to make it general for all Collections they are empty. May code deal with EmptyList so.
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());
}
}

