Elim的博客

用来记录一些原创性的总结


Elim

JAXB之XmlSeeAlso

@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日)