9.2. XML
正如您可能已经知道,Jersey 使用 MessageBodyWriter
9.2.1. Low level XML support 低级 XML 支持
Jersey 目前支持一些低水平的数据类型:StreamSource, SAXSource, DOMSource 和 Document。您可以使用这些类型的返回类型或方法(资源)参数。让说我们想要测试这个功能,我们有 helloworld示例 作为起点。所有我们需要做的就是添加方法(资源)的消耗和产生的 XML 和类型将使用上面提到的。
Example 8.31. Low level XML test - methods added to HelloWorldResource.java
@POST@Path("StreamSource")public StreamSource getStreamSource(StreamSource streamSource) {return streamSource;}@POST@Path("SAXSource")public SAXSource getSAXSource(SAXSource saxSource) {return saxSource;}@POST@Path("DOMSource")public DOMSource getDOMSource(DOMSource domSource) {return domSource;}@POST@Path("Document")public Document getDocument(Document document) {return document;}
MessageBodyWriter
curl -v http://localhost:8080/base/helloworld/StreamSource -d “
“
你应该从我们的服务得到完全相同的 XML ;在本例中,XML 头添加到响应但内容停留。自由的遍历所有资源。
9.2.2. Getting started with JAXB 开始
好的开始,人们已经有了一些 JAXB 注解的经验,例子是是 JAXB示例。你可以看到不同的用例。本文主要是针对那些没有 JAXB 经验。别指望所有可能的注释和他们的组合将在这一章,JAXB(JSR 222实现)是相当复杂和全面。但如果你只是想知道如何与 REST 服务交换 XML 消息,你看着合适的章节。
可以从简单的例子开始。让我们说我们有类 Planet 和服务生产的“Planets”。
Example 9.32. Planet class
@XmlRootElementpublic class Planet {public int id;public String name;public double radius;}
Example 9.33. Resource class
@Path("planet")public class Resource {@GET@Produces(MediaType.APPLICATION_XML)public Planet getPlanet() {final Planet planet = new Planet();planet.id = 1;planet.name = "Earth";planet.radius = 1.0;return planet;}}
你可以看到有一些额外的注释声明类 Planet,尤其是@XmlRootElement。这是一个 JAXB 注释的 java 类映射到 XML 元素。我们不需要指定任何其他,因为 Planet 非常简单的类,所有的字段都是公开的。在这种情况下,XML 元素名称将派生类名或你可以设置名称属性:@XmlRootElement(name=”yourName”)。
我们的资源类将响应 GET/planet 请求
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><planet><id>1</id><name>Earth</name><radius>1.0</radius></planet>
这可能正是我们想要的……与否。或者我们可能不关心,因为我们可以使用 JAX-RS 客户端发出请求该资源,这很容易:
Planet planet = webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(Planet.class);
有预先创建 WebTarget 对象指向我们的应用程序的上下文根,只需添加路径(在我们的例子中是planet),接收 header(不是强制性的,但服务可以提供不同的内容基于这头,例如可以为 text/html 在 web 浏览器),最后我们指定,我们预计 Planet 类通过GET请求。
不仅可能需要生成 XML ,我们可能希望使用它。
Example 9.34. Method for consuming Planet
@POST@Consumes(MediaType.APPLICATION_XML)public void setPlanet(Planet planet) {System.out.println("setPlanet " + planet);}
有效的请求后,服务将打印字符串表示的 Planet,可以像 Planet{id=2, name=’Mars’, radius=1.51}。通过 JAX-RS 客户端你能做到:
webTarget.path("planet").post(planet);
如果有需要其他(非默认的) XML 表示,其他 JAXB 注解需要被使用。简化这一过程通常是由从 XML 模式生成 java 源代码是通过 XML 到 java 编译器和它的 xjc 是 JAXB 的一部分。
9.2.3. POJOs
有时,你不能或者不想在代码里面使用注解,但又想用消费和生成 XML 的类的表现形式。这种情况下就可以用 JAXBElement 。下面例子就是没有用 @XmlRootElement 注解:
Example 9.35. Resource class - JAXBElement
@Path("planet")public class Resource {@GET@Produces(MediaType.APPLICATION_XML)public JAXBElement<Planet> getPlanet() {Planet planet = new Planet();planet.id = 1;planet.name = "Earth";planet.radius = 1.0;return new JAXBElement<Planet>(new QName("planet"), Planet.class, planet);}@POST@Consumes(MediaType.APPLICATION_XML)public void setPlanet(JAXBElement<Planet> planet) {System.out.println("setPlanet " + planet.getValue());}}
正如您可以看到的,一切都是用了 JAXBElement 就会复杂一些。这是因为现在需要显式地设置元素名称的给 Planet 类的 XML 表示。客户端比服务器端更加复杂,因为你不能做 JAXBElement<Planet> 所以 JAX-RS 客户端 API 提供了如何通过 声明 GenericType<T> 的子类 解决它
Example 9.36. Client side - JAXBElement
// GETGenericType<JAXBElement<Planet>> planetType = new GenericType<JAXBElement<Planet>>() {};Planet planet = (Planet) webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(planetType).getValue();System.out.println("### " + planet);// POSTplanet = new Planet();// ...webTarget.path("planet").post(new JAXBElement<Planet>(new QName("planet"), Planet.class, planet));
9.2.4. Using custom JAXBContext 使用自定义 JAXBContext
有些场景适合使用自定义 JAXBContext。JAXBContext 的创建是一个昂贵的操作,如果你已经创建了一个,相同的实例被 Jersey 使用。其他可能使用的情况是当你需要给 JAXBContext 建立一些特定的东西,例如设置不同的类装载器。
Example 9.37. PlanetJAXBContextProvider
@Providerpublic class PlanetJAXBContextProvider implements ContextResolver<JAXBContext> {private JAXBContext context = null;public JAXBContext getContext(Class<?> type) {if (type != Planet.class) {return null; // we don't support nothing else than Planet}if (context == null) {try {context = JAXBContext.newInstance(Planet.class);} catch (JAXBException e) {// log warning/error; null will be returned which indicates that this// provider won't/can't be used.}}return context;}}
上面示例简单创建 JAXBContext 的过程,所有你需要做的就是把这个@Provider 注释放上,这样 Jersey 就能找到它。用户有时在客户端使用 provider (提供者)类 出问题,所以只是为了提醒——你必须 在客户端配置(客户端做任何事情不像通过服务器包扫描)声明他们。
Example 9.38. Using Provider with JAX-RS client
ClientConfig config = new ClientConfig();config.register(PlanetJAXBContextProvider.class);Client client = ClientBuilder.newClient(config);
9.2.5. MOXy
如果你想使用 MOXy 作为 JAXB 实现而不是 JAXB RI 您有两种选择。您可以使用标准的 JAXB 机制来定义 从 JAXBContext 实例将获得(有关此主题的更多信息,读JavaDoc JAXBContext)的 JAXBContextFactory 或者你可以将 jersey-media-moxy 模块添加到您的项目和注册/配置 MoxyXmlFeature 类/实例的Configurable。
Example 9.39. Add jersey-media-moxy dependency.
<dependency><groupId>org.glassfish.jersey.media</groupId><artifactId>jersey-media-moxy</artifactId><version>2.16</version></dependency>
Example 9.40. Register the MoxyXmlFeature class.
final ResourceConfig config = new ResourceConfig().packages("org.glassfish.jersey.examples.xmlmoxy").register(MoxyXmlFeature.class);
Example 9.41. Configure and register an MoxyXmlFeature instance.
// Configure Properties.final Map<String, Object> properties = new HashMap<String, Object>();// ...// Obtain a ClassLoader you want to use.final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();final ResourceConfig config = new ResourceConfig().packages("org.glassfish.jersey.examples.xmlmoxy").register(new MoxyXmlFeature(properties,classLoader,true, // Flag to determine whether eclipselink-oxm.xml file should be used for lookup.CustomClassA.class, CustomClassB.class // Classes to be bound.));
