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
@XmlRootElementpublic class MyJaxbBean {public String name;public int age;public MyJaxbBean() {} // JAXB needs thispublic 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
@XmlRootElementpublic class MyJaxbBean {@XmlElement(name="king")public String name;@XmlTransientpublic 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
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
@Providerpublic class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {final ObjectMapper defaultObjectMapper;public MyObjectMapperProvider() {defaultObjectMapper = createDefaultMapper();}@Overridepublic 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
@XmlRootElementpublic 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
@XmlRootElementpublic 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
@Providerpublic 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);}@Overridepublic 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
@XmlRootElementpublic 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. 这里提供示例.
