摘要: JAXB 作为JDK的一部分,能便捷地将Java对象与XML进行相互转换,本教程从实际案例出发来讲解JAXB 2 的那些事儿。完整版目录
上一节,我使用 Spring Boot 搭建的工程,能够正确地返回JSON 数据,内容和JAXB没有关系,主要是为这一节打基础。
相对之前返回 json 格式数据,需要添加 MediaType 指定返回类型。
@GetMapping(value="/id/xml", produces=MediaType.APPLICATION_XML_VALUE)
public Student findById2(String id) {
return studentService.findById(id);
}
访问 http://localhost:8080/student/id/xml?id=11
。
结果并没有得到想要的数据,而是返回了错误页面。
查看Eclipse控制台,并没有任何错误输出,说明代码层面没有出问题。
查看页面控制台,发现错误码是 406,并且 Accept 已经是 application/xml
。
Spring 使用HttpMessageConverter
将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息。
并且HttpMessageConveter
提供了6个实现类,如下:
而对于消息转换器的调用,都是在RequestResponseBodyMethodProcessor
类中完成的。该类会根据注解中指定返回类型去找对应的转换器,如果找不到,就抛出HttpMediaTypeNotAcceptableException
异常,浏览器会收到一个406 状态码。
原来Spring已经实现了Jaxb的转换方法,那为什么会抛出异常呢?回想一下之前使用Jaxb的必要步骤,就知道此刻还忘记了一个重要流程。。。
赶紧加上这个必须的注解,在需要返回的对象上加 @XmlRootElement
。因为Jaxb是JDK自带的实现,不需要加入任何依赖 jar 包。
@XmlRootElement
public class Student {
...
}
此时,再次访问 http://localhost:8080/student/id/xml?id=11
发现正确返回了结果。
<student>
<age>23age>
<id>11id>
<name>Tomname>
student>
接下来,照葫芦画瓢,新加一个方法,和返回 json 数据时一样,只是添加了返回值格式为 XML 。
@GetMapping(value="/list/xml", produces=MediaType.APPLICATION_XML_VALUE)
public List<Student> findAll2(){
return studentService.findAll();
}
赶紧访问以下:http://localhost:8080/student/list/xml
又给了一个 406。明明已经加了注解,怎么还是不行呢?再次回想之前的章节,原来 JAXB 不支持 List 数据类型,看来需要对现有代码改造了。
首先,定义一个 Students
类,其中只有一个属性,就是List
,List
中存放Student
的数据,添加setter/getter方法,并且添加JAXB注解@XmlRootElement
.
@XmlRootElement
public class Students {
List<Student> student;
public List<Student> getStudent() {
return student;
}
public void setStudent(List<Student> student) {
this.student = student;
}
}
对于Controller也需要适当改造,需要的返回值不是List
了,而是Students
:
@GetMapping(value="/students/xml", produces=MediaType.APPLICATION_XML_VALUE)
public Students findAll3(){
Students students = new Students();
students.setStudent(studentService.findAll());
return students;
}
查看一下数据:http://localhost:8080/student/students/xml
<students>
<student>
<age>23age>
<id>11id>
<name>Tomname>
student>
<student>
<age>25age>
<id>12id>
<name>Jerryname>
student>
<student>
<age>32age>
<id>13id>
<name>Davidname>
student>
<student>
<age>41age>
<id>14id>
<name>Jackname>
student>
students>
总算达到了所需的效果,只是过程有点艰辛。
为了返回XML数据,代码可是比返回json多了许多,有什么方式可以少写点代码呢?当然有。只是这次需要添加一个 jar 包。
加入 jackson 的依赖,让 Spring 使用 jackson 来处理 XML 请求。
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
dependency>
添加完这个依赖以后,Spring会使用另外的一个转换器MappingJackson2HttpMessageConverter
来处理。
添加一个返回Student
的方法:
@GetMapping(value="/id/jackson/xml", produces=MediaType.APPLICATION_XML_VALUE)
public Student findById3(String id) {
return studentService.findById(id);
}
首先,去掉JAXB的注解@XmlRootElement
,然后访问:http://localhost:8080/student/id/jackson/xml?id=11
得到想要的结果:
<Student>
<id>11id>
<name>Tomname>
<age>23age>
Student>
值得注意的是,这个RootElement的名称是返回对象的类名,而不是像JAXB一样,返回首字母小写的类名。
这个方法和之前返回 json 格式的代码几乎一致,只是生命了返回类型为 XML 。
@GetMapping(value="/list/jackson/xml", produces=MediaType.APPLICATION_XML_VALUE)
public List<Student> findAll4(){
return studentService.findAll();
}
访问一下:http://localhost:8080/student/list/jackson/xml
<List>
<item>
<id>11id>
<name>Tomname>
<age>23age>
item>
<item>
<id>12id>
<name>Jerryname>
<age>25age>
item>
<item>
<id>13id>
<name>Davidname>
<age>32age>
item>
<item>
<id>14id>
<name>Jackname>
<age>41age>
item>
List>
竟然惊喜地发现,没有任何代码改造,就能返回想要的 XML 格式数据。比之前使用 JAXB 方式简单许多,这也是真正开发时所选用的方式。
当然,这里面有一些名称有可能和需要的结果不一致,jackson
同样提供了许多注解来满足定制化需求。
不过此时,再次访问一下之前返回 json 的方法:http://localhost:8080/student/id?id=11
。发现默认返回类型成为了XML,该方法返回了XML数据。
如果想要返回 json ,需要手动声明:
@GetMapping(value="/id2", produces=MediaType.APPLICATION_JSON_VALUE)
public Student findById4(String id) {
return studentService.findById(id);
}
访问一下:http://localhost:8080/student/id2?id=11
正确返回 json 数据。
观察之前写的代码:
片段1
@GetMapping(value="/id")
public Student findById(String id) {
return studentService.findById(id);
}
片段2
@GetMapping(value="/id/xml", produces=MediaType.APPLICATION_XML_VALUE)
public Student findById2(String id) {
return studentService.findById(id);
}
为了支持两种类型的输出,我们写了完全相同的两段代码,能不能通过一个参数的配置,来指定返回xml 还是 json 呢?尝试一下。
Java提供了一个WebMvcConfigurerAdapter
来处理与 mvc 相关的配置,我们可以通过修改这个配置来改变某些行为。
public class CustomWebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorParameter(true) // 是否支持参数化处理请求
.parameterName("format") // 参数的名称, 默认为format
.defaultContentType(MediaType.APPLICATION_JSON) // 全局的默认返回类型
.mediaType("xml", MediaType.APPLICATION_XML) // 参数值与对应的类型XML
.mediaType("json", MediaType.APPLICATION_JSON); // 参数值与对应的类型JSON
}
}
再次回忆一下最开始的第一个方法:
@GetMapping(value="/id")
public Student findById(String id) {
return studentService.findById(id);
}
不需要任何的特殊说明,是最优的代码。这次不需要任何改造。
访问一下:http://localhost:8080/student/id?id=12&format=json
得到的结果:
{"id":"12","name":"Jerry","age":25}
访问一下:http://localhost:8080/student/id?id=12&format=xml
得到的结果:
<Student>
<id>12id>
<name>Jerryname>
<age>25age>
Student>
我们只在最开始增加了一个统一的配置,之后的方法不用做任何改动就支持了 json 与 xml 两种格式。还可以通过扩展配置支持更多的格式。
访问一下:http://localhost:8080/student/id?id=12
得到了之前配置的默认格式。
访问一下:http://localhost:8080/student/id?id=12&format=xxx
如果给一个不支持的格式,直接返回406.
可以在GitHub找到完整代码。
本节代码均在该包下:package com.example.demo.lesson20;
关于 JAXB 在 Spring 中的使用就介绍完了,下一节将是整个系类课程的总结。