用来记录一些原创性的总结
@XmlSeeAlso用来定义在Class上,指定当前Class关联的Class。在之前介绍的对象生成XML时以子类的结构生成XML一文中,我们在生成XML时需要把对应的子类也传递到上下文中,因为此时对于使用@XmlRootElement标准的根类型来说,其中关联的是父类型,你如果不指定一个子类型,它是不知道具体的子类型是啥的。我们先来重温一下这个示例。
@XmlRootElement
class Response {
private int returnCode;
private String message;
private ResultData resultData;
public int getReturnCode() {
return returnCode;
}
public void setReturnCode(int returnCode) {
this.returnCode = returnCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@XmlElement(name = "data")
public ResultData getResultData() {
return resultData;
}
public void setResultData(ResultData resultData) {
this.resultData = resultData;
}
}
class ResultData {
private Data realData;
@XmlElementRef
public Data getRealData() {
return realData;
}
public void setRealData(Data realData) {
this.realData = realData;
}
}
class Data {
}
@XmlRootElement
class Person extends Data {
private Integer id;
private String name;
@XmlAttribute
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@XmlRootElement
class Dept extends Data {
private Integer id;
private String no;
private String name;
@XmlAttribute
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
上面是涉及到的基本的Class,其中Data有两个子类,Person和Dept,期望实际传递的对象是Person就按照Person的结构进行生成,传递的是Dept就按照Dept的结构进行生成。测试代码如下:
@Test
public void testPerson() throws Exception {
Person person = new Person();
person.setId(1);
person.setName("张三");
Response response = this.newResponse(person);
this.marshal(response, Response.class, Person.class);
}
@Test
public void testDept() throws Exception {
Dept dept = new Dept();
dept.setId(3);
dept.setNo("003001");
dept.setName("技术部");
Response response = this.newResponse(dept);
this.marshal(response, Response.class, Dept.class);
}
private void marshal(Response response, Class<?>... classesToBeBound) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(classesToBeBound);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
private Response newResponse(Data realData) {
Response response = new Response();
response.setReturnCode(100);
response.setMessage("AAAA");
ResultData resultData = new ResultData();
resultData.setRealData(realData);
response.setResultData(resultData);
return response;
}
Data类型是Person时,生成XML时传递的相关Class中就传递了Person.class,this.marshal(response, Response.class, Person.class);
。如果此时不传递Person.class,生成XML就会异常。因为Person.class对于Marshal时的上下文来说是未知的。基于Person生成的XML结构如下:
<response>
<message>AAAA</message>
<data>
<person id="1">
<name>张三</name>
</person>
</data>
<returnCode>100</returnCode>
</response>
其实如果我们不希望在生成XML时指定上下文中的Class中包含Person.class或Dept.class,即无论你传递的Data是Person.class还是Dept.class,在生成XML时指定的绑定Class都只有根类型Response.class,我们也可以通过XmlSeeAlso来指定,通常是指定在父类型Data上。这样在根据根类型Response.class可以找到Data.class,根据Data.class的@XmlSeeAlso又可以找到它的子类型Person.class和Dept.class。这样Person.class个Dept.class对于上下文来说都是已知的,就可以做正确的转换了。使用@XmlSeeAlso的配置如下:
@XmlSeeAlso({Person.class, Dept.class})
class Data {
}
这样进行XML转换的时候就可以不再指定子类型了。
@Test
public void testPerson() throws Exception {
Person person = new Person();
person.setId(1);
person.setName("张三");
Response response = this.newResponse(person);
this.marshal(response, Response.class);
}
@XmlSeeAlso只建议在子类型比较少的情况下使用,假设一个类型的子类型有几百个上千个都把它们定义在父类型的@XmlSeeAlso中会显得比较庞大,这种情形建议在运行时传递实际的子类型到上下文中。 @XmlSeeAlso的作用是让一些运行时传递的类型在JAXB上下文中可知,所以它不一定非得加在父类型上,但此种情形我们通常都会把它加在父类型上。
(本文由Elim写于2017年8月31日)