9.1. JSON
Jersey JSON 支持之际,一组扩展模块,每个模块包含一个功能的实现,需要注册到您的配置实例(客户机/服务器)。有多个框架提供支持 JSON 处理和/或 JSON-to-Java 绑定。下面列出的模块提供支持 JSON 表示通过整合个人 JSON 框架 Jersey。目前,Jersey 集成了以下模块提供 JSON 支持:
- MOXy-JSON 默认通过 MOXy来绑定,并且在 Jersey 2.0 以来的应用程序是支持 JSON 绑定首选方法 。当JSON MOXy 模块在 类路径,Jersey 将自动发现模块和无缝地支持 JSON 绑定支持通过 MOXy 在应用程序中。(见4.3节,“自动发现功能”。
- Java API为JSON处理(JSON-P)
- Jackson
- Jettison
9.1.1. Approaches to JSON Support 支持JSON方法
每个上述扩展模块使用一个或多个可用的三种基本方法在处理JSON表示:
- 基于 POJO 的 JSON 绑定
- 基于 JAXB 的 JSON 绑定
- 低级的JSON解析和处理支持
第一个方法是非常通用的,允许您将任何 Java 对象映射到 JSON,反之亦然。其他两种方法限制你在 Java 类型资源方法可以生产和/或使用。基于JAXB 方法是有用的,如果你打算使用 JAXB 的某些特性和支持 XML 和JSON 表示。最后,低级方法给你最好的细粒度控制输出的 JSON 数据格式。
9.1.1.1. POJO support 基于 POJO
POJO的支持是最简单的方法将 Java 对象转换为 JSON 和转回去。
媒体模块,支持这种方法是 MOXy 和 Jackson
9.1.1.2. JAXB based JSON support 基于 JAXB
采取这种方法可以节省大量的时间,如果你想轻松地生成/使用 JSON 和 XML 数据格式。与 JAXB bean 你将能够使用相同的 Java 模型生成JSON和 XML 表示。与这样一个合作的另一个优点是简单模型和 API 在 Java SE 平台的可用性。 JAXB 使用注解的 POJO,这些可以处理简单的 Java bean。
基于 JAXB 方法的一个缺点可能是如果你需要使用一个非常具体的 JSON 格式。然后可能很难找到一个合适的方法来得到这样一个格式生产和消费。这是一个原因提供了许多配置选项,这样你就可以控制如何 JAXB bean 序列化和反序列化。额外的配置选项但是需要你更详细的了解您所使用的框架。
下面是一个非常简单的例子,来说明JAXB bean可能看起来像。
Example 9.1. Simple JAXB bean implementation
@XmlRootElement
public class MyJaxbBean {
public String name;
public int age;
public MyJaxbBean() {} // JAXB needs this
public MyJaxbBean(String name, int age) {
this.name = name;
this.age = age;
}
}
使用上面的 JAXB bean 生成 JSON 数据格式资源方法,然后一样简单:
Example 9.2. JAXB bean used to generate JSON representation
@GET
@Produces("application/json")
public MyJaxbBean getMyBean() {
return new MyJaxbBean("Agamemnon", 32);
}
注意,JSON @Produces 注释中指定特定的mime类型,MyJaxbBean 的方法返回一个实例,JAXB 能够处理。生成的 JSON 在这种情况下会看起来像:
{"name":"Agamemnon", "age":"32"}
正确使用 JAXB 注解本身可以控制一定 JSON 格式输出。具体来说,直接通过使用 JAXB 注释很容易做到重命名和删除属性。例如,下面的例子描述了上述 MyJaxbBean 变化将导致 {“king”:”Agamemnon”} JSON输出。
Example 9.3. Tweaking JSON format using JAXB
@XmlRootElement
public class MyJaxbBean {
@XmlElement(name="king")
public String name;
@XmlTransient
public int age;
// several lines removed
}
媒体模块,支持这种方法是 MOXy, Jackson,Jettison
9.1.1.3. Low-level based JSON support 低级的JSON解析和处理支持
JSON 处理 API 是一个新的标准 API 进行解析和处理 JSON 结构以类似的方式,SAX 和 StAX 解析器提供对 XML 。这个 API 是Java EE 7 和后来的一部分。另一个 JSON 解析/处理抛弃框架提供的 API。这两种 api 提供一个低级访问生产和消费 JSON 数据结构。采用这种低级的方法你会使用JsonObject(或JsonObject) 和/或 JsonArray (或分别 JsonArray )类在处理JSON数据表示。
这些低级 api 的最大优势是,你会得到完全控制和消费产生的 JSON 格式。你也能够生产和消费非常大的 JSON 结构使用流 JSON 解析器/生成器api。另一方面,处理您的数据模型对象可能会更复杂,相对于 POJO 或基于JAXB 绑定方法。差异是描述在以下代码片段。
基于JAXB 绑定方法
Example 9.4. JAXB bean creation
MyJaxbBean myBean = new MyJaxbBean("Agamemnon", 32);
当你构建一个 JAXB bean 时,JSON 写成 {“name”:”Agamemnon”, “age”:32}
现在构建一个等价的 JsonObject / JsonObject(生成的JSON的表达式),您需要几行代码。下面的例子说明了如何构造相同的 JSON 数据使用标准的Java EE 7 JSON处理API。
JsonObject myObject = Json.createObjectBuilder()
.add("name", "Agamemnon")
.add("age", 32)
.build();
最后看下使用 Jettison 来做同样的事,
Example 9.6. Constructing a JSONObject (Jettison)
JSONObject myObject = new JSONObject();
try {
myObject.put("name", "Agamemnon");
myObject.put("age", 32);
} catch (JSONException ex) {
LOGGER.log(Level.SEVERE, "Error ...", ex);
}
媒体模块,支持低级 JSON 解析和生成方法是 Java API for JSON Processing (JSON-P)和Jettison。除非你有强烈的理由使用非标准抛Jettison API,我们推荐您使用新标准Java API for JSON Processing (JSON-P) API。
9.1.2. MOXy
9.1.2.1. Dependency
需要添加 jersey-media-moxy 依赖库在你的 pom.xml 来使用 MOXy
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.16</version>
</dependency>
不用 maven 的话,要确保所有需要的库在类路径下,建jersey-media-moxy
9.1.2.2. Configure and register 配置和注册
如上所述在见4.3节,“自动发现功能”以及在本章早些时候,MOXy 模块是您不需要显式地注册它的特性(MoxyJsonFeature)在您的客户端/服务器配置的模块之一,这个特性是自动发现和注册时将 jersey-media-moxy 模块添加到您的类路径。
自动发现的 jersey-media-moxy 模块定义了几个属性,可用于控制自动登记 MoxyJsonFeature(除了通用CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE一个客户机/服务器变量):
- CommonProperties.MOXY_JSON_FEATURE_DISABLE
- ServerProperties.MOXY_JSON_FEATURE_DISABLE
- ClientProperties.MOXY_JSON_FEATURE_DISABLE
注意
手动注册其他Jersey JSON提供者功能(除了Java API for JSON Processing (JSON-P)) 禁用MoxyJsonFeature的自动启用和配置。
配置 MOXy 所提供的MessageBodyReader
- MoxyJsonConfig#property(java.lang.String, java.lang.Object)) ——设置 Marshaller 和 Unmarshaller属性值
- MoxyJsonConfig#marshallerProperty(java.lang.String, java.lang.Object)) ——设置 Marshaller 属性值
- MoxyJsonConfig#unmarshallerProperty(java.lang.String, java.lang.Object)) ——设置 Unmarshaller属性值
Example 9.7. MoxyJsonConfig - Setting properties.
final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
final MoxyJsonConfig configuration = new MoxyJsonConfig()
.setNamespacePrefixMapper(namespacePrefixMapper)
.setNamespaceSeparator(':');
为了使 MoxyJsonConfig 对 MOXy 可见,您需要创建并注册ContextResolver
Example 9.8. Creating ContextResolver
final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
final MoxyJsonConfig moxyJsonConfig = MoxyJsonConfig()
.setNamespacePrefixMapper(namespacePrefixMapper)
.setNamespaceSeparator(':');
final ContextResolver<MoxyJsonConfig> jsonConfigResolver = moxyJsonConfig.resolver();
配置属性传递给底层 MOXyJsonProvider 的另一种方法是设置直接到您的配置实例(参见下面的一个例子)。这些都是被属性设置覆盖到 MoxyJsonConfig 。
Example 9.9. Setting properties for MOXy providers into Configurable
new ResourceConfig()
.property(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ".")
// further configuration
当 MOXy的 MessageBodyReader
Table 9.1. Default property values for MOXy MessageBodyReader
javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT org.eclipse.persistence.jaxb.JAXBContextProperties#JSON_INCLUDE_ROOT
false org.eclipse.persistence.jaxb.MarshallerProperties#JSON_MARSHAL_EMPTY_COLLECTIONStrue org.eclipse.persistence.jaxb.JAXBContextProperties#JSON_NAMESPACE_SEPARATORorg.eclipse.persistence.oxm.XMLConstants#DOT
Example 9.10. Building client with MOXy JSON feature enabled.
final Client client = ClientBuilder.newBuilder()
// The line below that registers MOXy feature can be
// omitted if FEATURE_AUTO_DISCOVERY_DISABLE is
// not disabled.
.register(MoxyJsonFeature.class)
.register(jsonConfigResolver)
.build();
Example 9.11. Creating JAX-RS application with MOXy JSON feature enabled.
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jsonmoxy")
// The line below that registers MOXy feature can be
// omitted if FEATURE_AUTO_DISCOVERY_DISABLE is
// not disabled.
.register(MoxyJsonFeature.class)
.register(jsonConfigResolver);
9.1.2.3. Examples
Jersey 提供一个 JSON MOXy example如何使用 MOXy 来消费/生成JSON。
8.1.3. Java API for JSON Processing (JSON-P)
8.1.3.1. Dependency 依赖
使用 JSON-P 作为 JSON 的提供者需要添加 jersey-media-json-processing 模块到 pom.xml 文件:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.16</version>
</dependency>
如果你不使用 Maven,要确保所有需要的依赖关系(见jersey-media-json-processing)到类的路径。
9.1.3.2. Configure and register 配置和注册
正如见4.3节,“自动发现功能”中提到的,JSON-Processing 模块,您不需要显式地注册它的特性(JsonProcessingFeature)在您的客户端/服务器配置,这个特性是将jersey-media-json-processing 模块添加到您的类路径中时自动发现和注册时。
至于其他模块,jersey-media-json-processing还几个属性,会影响JsonProcessingFeature 的注册 (除了CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE等):
- CommonProperties.JSON_PROCESSING_FEATURE_DISABLE
- ServerProperties.JSON_PROCESSING_FEATURE_DISABLE
- ClientProperties.JSON_PROCESSING_FEATURE_DISABLE
JSON-P 提供配置 MessageBodyReader
- JsonGenerator.PRETTY_PRINTING (“javax.json.stream.JsonGenerator.prettyPrinting”)
Example 9.12. Building client with JSON-Processing JSON feature enabled.
ClientBuilder.newClient(new ClientConfig()
// The line below that registers JSON-Processing feature can be
// omitted if FEATURE_AUTO_DISCOVERY_DISABLE is not disabled.
.register(JsonProcessingFeature.class)
.property(JsonGenerator.PRETTY_PRINTING, true)
);
Example 9.13. Creating JAX-RS application with JSON-Processing JSON feature enabled.
// Create JAX-RS application.
final Application application = new ResourceConfig()
// The line below that registers JSON-Processing feature can be
// omitted if FEATURE_AUTO_DISCOVERY_DISABLE is not disabled.
.register(JsonProcessingFeature.class)
.packages("org.glassfish.jersey.examples.jsonp")
.property(JsonGenerator.PRETTY_PRINTING, true);
9.1.3.3. Examples
Jersey 提供了一个JSON Processing实例如何使用 JSON-Processing 处理消费/生成JSON。
9.1.4. Jackson (1.x and 2.x)
9.1.4.1. Dependency 依赖
使用 Jackson 2.x 需添加 jersey-media-json-jackson 模块到 pom.xml:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.16</version>
</dependency>
使用 Jackson 1.x 用法如下:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson1</artifactId>
<version>2.16</version>
</dependency>
如果你不使用 Maven,要确保所有需要的依赖关系(见 jersey-media-json-jackson 或 jersey-media-json-jackson) )到类的路径。
9.1.4.2. Configure and register 配置和注册
注意
注意,不同的名称空间,Jackson 1.x (org.codehaus.jackson) 和 Jackson 2.x (com.fasterxml.jackson)
Jackson JSON 处理器可以通过提供一个自定义Jackson 2 的ObjectMapper (或者 Jackson 1的 ObjectMapper ) 实例来控制。这可能是方便的,如果你需要重新定义默认 Jackson 行为和调整你的 JSON数据结构。Jackson 的所有特性的详细描述了本指南的范围。下面的例子给你一个提示如何写 ObjectMapper (ObjectMapper)实例 到你的 Jersey 的应用程序。
如果需要,在你的
配置(客户机/服务器)中,为了使用 Jackson 作为JSON(JAXB/POJO)提供者需要给 ObjectMapper注册JacksonFeature(Jackson1Feature)和 ContextResolver
Example 9.14. ContextResolver
@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
final ObjectMapper defaultObjectMapper;
public MyObjectMapperProvider() {
defaultObjectMapper = createDefaultMapper();
}
@Override
public ObjectMapper getContext(Class<?> type) {
return defaultObjectMapper;
}
}
private static ObjectMapper createDefaultMapper() {
final ObjectMapper result = new ObjectMapper();
result.configure(Feature.INDENT_OUTPUT, true);
return result;
}
// ...
}
完整示例,见 来自 JSON-Jackson 例子中的 MyObjectMapperProvider 类.
Example 9.15. Building client with Jackson JSON feature enabled.
final Client client = ClientBuilder.newBuilder()
.register(MyObjectMapperProvider.class) //无特殊要求无需注册这个
.register(JacksonFeature.class)
.build();
Example 9.16. Creating JAX-RS application with Jackson JSON feature enabled.
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages(“org.glassfish.jersey.examples.jackson”)
.register(MyObjectMapperProvider.class) //无特殊要求无需注册这个
.register(JacksonFeature.class);
9.1.4.3. Examples
Jersey 提供 JSON Jackson (2.x) 的例子和 JSON Jackson (1.x) 例子 展示如何使用 Jackson 消费/生成JSON。
9.1.5. Jettison
Jettison 模块提供 (反)序列化 JSON 的 JAXB 方法,除了使用纯 JAXB,配置选项可以设置在一个JettisonConfig 实例。然后实例可以进一步用于创建 JettisonJaxbContext,作为主要的配置点。通过你的专业 JettisonJaxbContext to Jersey,你将最终需要实现一个JAXBContext ContextResolver
9.1.5.1. Dependency 依赖
如果使用 Jettison 需要添加 jersey-media-json-jettison 模块到 pom.xml :
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>2.16</version>
</dependency>
如果没有使用 Maven ,确保所有依赖库 (详见 jersey-media-json-jettison) 在 classpath 中.
9.1.5.2. JSON Notations 符号
JettisonConfig 允许你使用两种 JSON 符号,每种序列化 JSON 的方式是不同的。下面是支持符号的列表:
- JETTISON_MAPPED (默认符号)
- BADGERFISH
在处理更复杂的XML文档,你可能想要使用这些符号。即当你在JAXB bean处理多个XML名称空间。
独立的符号及其进一步的配置选项如下所述。而不是解释规则映射 XML 结构转换为JSON,描述的符号将使用一个简单的例子。以下是JAXB bean,它将被使用。
Example 9.17. JAXB beans for JSON supported notations description, simple address bean
@XmlRootElement
public class Address {
public String street;
public String town;
public Address(){}
public Address(String street, String town) {
this.street = street;
this.town = town;
}
}
Example 9.18. JAXB beans for JSON supported notations description, contact bean
@XmlRootElement
public class Contact {
public int id;
public String name;
public List<Address> addresses;
public Contact() {};
public Contact(int id, String name, List<Address> addresses) {
this.name = name;
this.id = id;
this.addresses =
(addresses != null) ? new LinkedList<Address>(addresses) : null;
}
}
以下文本主要工作是 contact bean 初始化:
Example 9.19. JAXB beans for JSON supported notations description, initialization
Address[] addresses = {new Address("Long Street 1", "Short Village")};
Contact contact = new Contact(2, "Bob", Arrays.asList(addresses));
例子中 contact bean 的 id=2, name=”Bob” 包含一个 address (street=”Long Street 1”, town=”Short Village”).
下面所有的配置选项描述的记录也在 JettisonConfig api文档
9.1.5.2.1. Jettison mapped notation 隐射符号
如果你需要处理各种 XML 名称空间,你会发现 Jettison 映射符号非常有用。允许定义一个特定名称空间 id 项:
...
@XmlElement(namespace="http://example.com")
public int id;
...
然后你只需配置从 XML 名称空间映射到 JSON 前缀如下:
Example 9.20. XML namespace to JSON mapping configuration for Jettison based mapped notation
Map<String,String> ns2json = new HashMap<String, String>();
ns2json.put("http://example.com", "example");
context = new JettisonJaxbContext(
JettisonConfig.mappedJettison().xml2JsonNs(ns2json).build(),
types);
JSON 的结果就像下面的例子.
Example 9.21. JSON expression with XML namespaces mapped into JSON
{
"contact":{
"example.id":2,
"name":"Bob",
"addresses":{
"street":"Long Street 1",
"town":"Short Village"
}
}
}
请注意,该 id 项变成了 example.id 基于XML名称空间映射的id。如果你有更多的 XML 名称空间的 XML ,您需要为所有这些配置合适的映射。
Jersey 版本 2.2 中引入另一个可配置的选项与序列化 JSON 数组与Jettison的映射的符号。当序列化元素代表单项列表/数组时,您可能想要使用以下 Jersey 配置方法来显式地名称元素将其视为数组不管实际内容是什么。
Example 9.22. JSON Array configuration for Jettison based mapped notation
context = new JettisonJaxbContext(
JettisonConfig.mappedJettison().serializeAsArray("name").build(),
types);
JSON 结果想下面例子,不重要的行已经删除
Example 9.23. JSON expression with JSON arrays explicitly configured via Jersey
{
"contact":{
...
"name":["Bob"],
...
}
}
9.1.5.2.2. Badgerfish notation
从 JSON 和 JavaScript 的角度来看,这种表示法绝对是最可读的。您可能不希望使用它,除非你需要确保你的 JAXB bean 可以完美地读写和JSON ,无需顾及任何格式配置中,名称空间等。
JettisonConfig 使用 badgerfish 符号可以通过下面语句创建
JettisonConfig.badgerFish().build()
JSON 输出如下:
Example 9.24. JSON expression produced using badgerfish notation
{
"contact":{
"id":{
"$":"2"
},
"name":{
"$":"Bob"
},
"addresses":{
"street":{
"$":"Long Street 1"
},
"town":{
"$":"Short Village"
}
}
}
}
9.1.5.3. Configure and register 配置和注册
若使用 Jettison 为你的 JSON (JAXB/POJO) 提供者,需给 JAXBContext(如果需要) 注册 JettisonFeature 和 ContextResolver
Example 9.25. ContextResolver
@Provider
public class JaxbContextResolver implements ContextResolver<JAXBContext> {
private final JAXBContext context;
private final Set<Class<?>> types;
private final Class<?>[] cTypes = {Flights.class, FlightType.class, AircraftType.class};
public JaxbContextResolver() throws Exception {
this.types = new HashSet<Class<?>>(Arrays.asList(cTypes));
this.context = new JettisonJaxbContext(JettisonConfig.DEFAULT, cTypes);
}
@Override
public JAXBContext getContext(Class<?> objectType) {
return (types.contains(objectType)) ? context : null;
}
}
Example 9.26. Building client with Jettison JSON feature enabled.
final Client client = ClientBuilder.newBuilder()
.register(JaxbContextResolver.class) // No need to register this provider if no special configuration is required.
.register(JettisonFeature.class)
.build();
Example 9.27. Creating JAX-RS application with Jettison JSON feature enabled.
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jettison")
.register(JaxbContextResolver.class) // No need to register this provider if no special configuration is required.
.register(JettisonFeature.class);
9.1.5.4. Examples 例子
Jersey 提供 JSON Jettison 的例子.
8.1.6. @JSONP - JSON with Padding Support
Jersey 提供 开箱即用的支持 JSONP - JSON with Padding。以下条件必须满足利用此功能:
- 资源的方法,它应该返回 JSON,包装需要由 @JSONP 注释的。
- MessageBodyWriter
application/json 媒体类型,也接受资源方法的返回类型,需要注册(见本章JSON部分)。 - 用户的请求必须包含 Accept 标头的 JavaScript 定义媒体类型(见下文)。
可接受的媒体类型兼容 @JSONP 是:pplication/javascript, application/x-javascript, application/ecmascript, text/javascript, text/x-javascript, text/ecmascript, text/jscript.
Example 9.28. Simplest case of using @JSONP
@GET
@JSONP
@Produces({"application/json", "application/javascript"})
public JaxbBean getSimpleJSONP() {
return new JaxbBean("jsonp");
}
假设我们有注册一个 JSON 提供者和 JaxbBean 看起来像:
Example 9.29. JaxbBean for @JSONP example
@XmlRootElement
public class JaxbBean {
private String value;
public JaxbBean() {}
public JaxbBean(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
}
当你发送一个 GET 请求接受标题设置为 application/javascript 你会得到一个结果实体看起来像:
callback({
"value" : "jsonp",
})
当然,方法配置包装方法返回的实体默认回调可以看到在前面的例子。@JSONP 有两个参数,可以配置:回调,queryParam。回调的名称代表JavaScript 应用程序定义的回调函数。queryParam,第二个参数定义的名称查询参数的回调函数的名称使用在请求(如果存在)。queryParam 值默认为__callback,所以即使你不自己设置查询参数的名称,客户总是可以影响结果包装 JavaScript 回调方法的名称。
注意
queryParam 值(如果设置)总是优先于回调函数值。
稍微改下代码
Example 9.30. Example of @JSONP with configured parameters.
@GET
@Produces({"application/json", "application/javascript"})
@JSONP(callback = "eval", queryParam = "jsonpCallback")
public JaxbBean getSimpleJSONP() {
return new JaxbBean("jsonp");
}
两次提交:
curl -X GET http://localhost:8080/jsonp
将返回
eval({
"value" : "jsonp",
})
以及
curl -X GET http://localhost:8080/jsonp?jsonpCallback=alert
将返回
alert({
"value" : "jsonp",
})
Example. 这里提供示例.