JAXB中提供了XmlJavaTypeAdapter类,用于将JAXB不知道如何进行映射的类转换到可以映射的类。
XmlJavaTypeAdapter使用XmlAdapter接口来转换。XmlAdapter接口定义如下:
public abstract class XmlAdapter<ValueType,BoundType> {
/**
* Do-nothing constructor for the derived classes.
*/
protected XmlAdapter() {}
/**
* Convert a value type to a bound type.
*
* @param v
* The value to be converted. Can be null.
* @throws Exception
* if there's an error during the conversion. The caller is responsible for
* reporting the error to the user through {@link javax.xml.bind.ValidationEventHandler}.
*/
public abstract BoundType unmarshal(ValueType v) throws Exception;
/**
* Convert a bound type to a value type.
*
* @param v
* The value to be convereted. Can be null.
* @throws Exception
* if there's an error during the conversion. The caller is responsible for
* reporting the error to the user through {@link javax.xml.bind.ValidationEventHandler}.
*/
public abstract ValueType marshal(BoundType v) throws Exception;
}
可以看到XmlAdapter提供两个方法:marshal和unmarshal。注意marshal是把ValueType转成BoundType,unmarshal是反之。接下来就可以写样例代码了,假设有Person类如下:
public static class Person {
private String name;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
我们有House类使用它:
public static class House {
private Person host;
public Person getHost() {
return host;
}
public void setHost(Person host) {
this.host = host;
}
}
假设我们的xml当中使用字符来保存Person,那么就需要使用Adapter将字串转成Person类,因此可以标记House类如下:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class House {
@XmlJavaTypeAdapter(PersonAdapter.class)
private Person host;
public Person getHost() {
return host;
}
public void setHost(Person host) {
this.host = host;
}
}
注意host使用了Adapter:
@XmlJavaTypeAdapter(PersonAdapter.class)
private Person host;
PersonAdapter的代码如下:
public static class PersonAdapter extends XmlAdapter<String, Person> {
@Override
public Person unmarshal(String str) throws Exception {
Person p = new Person();
p.setName(str.split(":")[0]);
p.setGender(str.split(":")[1]);
return p;
}
@Override
public String marshal(Person person) throws Exception {
return (person.getName() + ":" + person.getGender());
}
}
我们定义输入输出的Person的字串格式为:"name:gender",因此在marshal与unmarshal里面有相关处理。
最后撰写测试代码:
public static void main(String[] args) throws Exception {
JAXBContext ctx = JAXBContext.newInstance(House.class);
House house = new House();
Person host = new Person();
host.setName("Weinan");
host.setGender("Male");
house.setHost(host);
ctx.createMarshaller().marshal(house, System.out);
}
输出如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><house><host>Weinan:Male</host></house>
最后想说说这里:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class House {
其中XmlAccessorType用来标记House类中什么元素需要被marshal和unmarshal,我们指定为XmlAccessType.FIELD,默认是XmlAccessType.PUBLIC_MEMBER。因为我们的House类中同时有getter和field,不指定为FIELD执行时会产生以下错误:
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "host"
this problem is related to the following location:
at public xml.PlayWithXmlTypeAdapter$Person xml.PlayWithXmlTypeAdapter$House.getHost()
...
Process finished with exit code 1