本文针对使用JAXB实现JavaBean与XML之间的相互转换,基本涵盖所有场景。
概念
JAXB是Java Architecture for XML Binding的缩写。可以将一个Java对象转变成为XML格式,反之亦然。
我们把对象与关系数据库之间的映射称为ORM,其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping)。原来JAXB是Java EE的一部分,在JDK1.6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK1.6中自带的这个JAXB版本是2.0,比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。
常见的注解说明
JAXBContext类:是应用的入口,用于管理XML/Java绑定信息。
Marshaller接口:将Java对象序列化为XML数据。
Unmarshaller接口:将XML数据反序列化为Java对象。
@XmlRootElement:将Java类或枚举类型映射到XML元素,用在Java类上,用于标注该类是xml的一个根节点。
@XmlElement:将Java类的一个属性映射到与属性同名的一个XML元素。通常与@XmlTransient搭配使用。
@XmlTransient:通常与 @XmlElement 须搭配使用的。@XmlElement用在属性上,用于指定生成xml的节点名,@XmlTransient用在对应的getter方法上,起到关联的作用。
@XmlElementWrapper :对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。通常配合XmlElement一起使用,XmlElementWrapper指定数组名,XmlElement指定生成xml的节点名。
@XmlElementRef:用在类属性的getter方法上(即该属性是一个JavaBean),并且该属性是某些子类的父类,起到引用的作用。同时标注得有@XmlElementRef的类属性,其子类上需要使用@XmlRootElement标注,否则转换异常,提示找不到具体的引用实现。另外,转换时,需要将其子类的class一起传递到JAXBContext上下文中,否则也无法转换。
@XmlAccessorOrder:控制JAXB 绑定类中属性和字段的排序。
@XmlType:将Java类或枚举类型映射到XML模式类型
@XmlAccessorType(XmlAccessType.FIELD) :控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标 注)字段到XML。还有XmlAccessType.PROPERTY和XmlAccessType.NONE。
@XmlJavaTypeAdapter:使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。
@XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。
1.工具方法
public class XmlBeanUtils {
/**
* Java Bean 转 Xml
*
* @param bean - Java Bean
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - xml
*/
public static String beanToXml(Object bean, Class<?>... inheritClazz) {
try {
JAXBContext context = initContext(bean.getClass(), inheritClazz);
Marshaller marshaller = context.createMarshaller();
// 格式化xml
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
// 是否去掉xml头
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
StringWriter writer = new StringWriter();
marshaller.marshal(bean, writer);
return writer.toString();
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
/**
* Xml 转 Java Bean
*
* @param xml - xml
* @param beanClazz - Java Bean Class
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - bean
*/
public static Object xmlToBean(String xml, Class<?> beanClazz, Class<?>... inheritClazz) {
try {
JAXBContext context = initContext(beanClazz, inheritClazz);
Unmarshaller um = context.createUnmarshaller();
StringReader sr = new StringReader(xml);
return um.unmarshal(sr);
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
/**
* 初始化JAXBContext
*
* @param mainClazz - 序列化或反序列化Class
* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class
* @return - JAXBContext
*/
private static JAXBContext initContext(Class<?> mainClazz, Class<?>... inheritClazz) throws JAXBException {
JAXBContext context;
if (inheritClazz != null) {
Class<?>[] clazzArr = new Class[inheritClazz.length + 1];
clazzArr[0] = mainClazz;
System.arraycopy(inheritClazz, 0, clazzArr, 1, clazzArr.length - 1);
context = JAXBContext.newInstance(clazzArr);
} else {
context = JAXBContext.newInstance(mainClazz);
}
return context;
}
}
2.应用实例
1.1.普通对象的转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
* 简单JavaBean与XML转换
*/
public class SimpleTest {
public static void main(String[] args) {
Cat cat = new Cat("猫咪", "爱吃鱼");
String xml = XmlBeanUtils.beanToXml(cat);
System.out.println("bean->xml = " + xml);
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Cat.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.2.有继承关系的对象转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
* 继承关系JavaBean与XML转换
*/
public class InheritBeanTest {
public static void main(String[] args) {
// 具有继承关系,使用Cat 或 Animal 接收都可以。
// Cat cat = new Cat("猫咪", "爱吃鱼");
Animal cat = new Cat("猫咪", "爱吃鱼");
String xml = XmlBeanUtils.beanToXml(cat);
System.out.println("bean->xml = " + xml);
// 此处不能使用父类Animal来接收,因为无法推断具体的实现类是哪个,必须使用具体的实现类Cat来接收
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Cat.class));
}
@Setter
abstract static class Animal {
// 不建议定义属性,因为从XML->Bean时,父类属性会丢失
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat extends Animal {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.3.嵌套的类属性是普通对象的转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
* 有嵌套关系的JavaBean与XML转换
*/
public class NestBeanTest {
public static void main(String[] args) {
Cat cat = new Cat("猫咪", "爱吃鱼");
Person person = new Person("貂蝉", 18, cat);
String xml = XmlBeanUtils.beanToXml(person);
System.out.println("bean->xml = " + xml);
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Person.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Person")
static class Person {
@XmlElement(name = "UserName")
private String name;
@XmlElement(name = "Age")
private int age;
// 嵌套的JavaBean
@XmlElement(name = "Pet")
private Cat cat;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public int getAge() {
return age;
}
@XmlTransient
public Cat getCat() {
return cat;
}
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.4.嵌套类属性是的集合(数组)普通对象的转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.util.ArrayList;
import java.util.List;
/**
* 有嵌套关系的集合JavaBean与XML转换
*/
public class NestListBeanTest {
public static void main(String[] args) {
List<Cat> cats = new ArrayList<>();
cats.add(new Cat("大猫咪", "爱吃大鱼"));
cats.add(new Cat("小猫咪", "爱吃小鱼"));
Person person = new Person("貂蝉", 18, cats);
String xml = XmlBeanUtils.beanToXml(person);
System.out.println("bean->xml = " + xml);
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Person.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Person")
static class Person {
@XmlElement(name = "UserName")
private String name;
@XmlElement(name = "Age")
private int age;
// 嵌套的集合JavaBean:XmlElementWrapper指定集合数组XML节点名,XmlElement指定具体的节点名
@XmlElementWrapper(name = "PetArray")
@XmlElement(name = "Pet")
private List<Cat> cats;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public int getAge() {
return age;
}
@XmlTransient
public List<Cat> getCats() {
return cats;
}
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.5.嵌套的类属性是继承关系对象的转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
* 有嵌套且有继承关系的JavaBean与XML转换
*/
public class NestInheritBeanTest {
public static void main(String[] args) {
// 此处使用Animal或Cat来接收都是可以的,因为已经创建了具体的Bean
Animal cat = new Cat("猫咪", "爱吃鱼");
Person person = new Person("貂蝉", 18, cat);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
String xml = XmlBeanUtils.beanToXml(person, Cat.class);
System.out.println("bean->xml = " + xml);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Person.class, Cat.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Person")
static class Person {
@XmlElement(name = "UserName")
private String name;
@XmlElement(name = "Age")
private int age;
// 嵌套的JavaBean,且是父类,此处不能使用@XmlElement标注,否则会报错,
// 原因是父类不知道具体的子类实现,需要在子类中使用@XmlRootElement标注具体的实现
private Animal animal;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public int getAge() {
return age;
}
// 必须使用@XmlElementRef来标注父类,起到引用的作用
@XmlElementRef
public Animal getAnimal() {
return animal;
}
}
@Setter
abstract static class Animal {
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat extends Animal {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.6.嵌套的类属性是集合(数组)继承关系对象的转换
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 有嵌套且有继承关系的集合JavaBean与XML转换
*/
public class NestInheritListBeanTest {
public static void main(String[] args) {
// 此处使用Animal来接,属于List泛型知识,此处不再赘述
List<Animal> animals = new ArrayList<>();
animals.add(new Cat("大猫咪", "爱吃大鱼"));
animals.add(new Cat("小猫咪", "爱吃小鱼"));
Person person = new Person("貂蝉", 18, animals);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
String xml = XmlBeanUtils.beanToXml(person, Cat.class);
System.out.println("bean->xml = " + xml);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Person.class, Cat.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Person")
static class Person {
@XmlElement(name = "UserName")
private String name;
@XmlElement(name = "Age")
private int age;
// 嵌套的JavaBean,且是父类,此处不能使用@XmlElement标注,否则会报错,
// 原因是父类不知道具体的子类实现,需要在子类中使用@XmlRootElement标注具体的实现
private List<Animal> animals;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public int getAge() {
return age;
}
// 必须使用@XmlElementRef来标注父类,起到引用的作用
// 必须使用@XmlElementWrapper指定集合数组XML节点名
@XmlElementRef
@XmlElementWrapper(name = "PetArray")
public List<Animal> getAnimals() {
return animals;
}
}
@Setter
abstract static class Animal {
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat extends Animal {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}
1.7.有继承关系且父类有属性的对象的转换
(不推荐使用,XML->Bean转换时父类属性会丢失)
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
* 有嵌套且有继承关系的JavaBean与XML转换
* 且父类中定义了属性,XML->Bean,父类属性会丢失(除非必要,不要再父类中定义属性)
*/
public class NoSupportNestInheritBeanTest {
public static void main(String[] args) {
// 此处使用Animal或Cat来接收都是可以的,因为已经创建了具体的Bean
Cat cat = new Cat("猫咪", "爱吃鱼");
cat.setCountry("中国");
Person person = new Person("貂蝉", 18, cat);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
String xml = XmlBeanUtils.beanToXml(person, Cat.class);
System.out.println("bean->xml = " + xml);
// 转换时需要将Person中Animal类属性的实现类Cat.class传入。否则无法无法转换,因为不清楚具体的实现。
System.out.println("xml->bean = " + XmlBeanUtils.xmlToBean(xml, Person.class, Cat.class));
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Person")
static class Person {
@XmlElement(name = "UserName")
private String name;
@XmlElement(name = "Age")
private int age;
// 嵌套的JavaBean,且是父类,此处不能使用@XmlElement标注,否则会报错,
// 原因是父类不知道具体的子类实现,需要在子类中使用@XmlRootElement标注具体的实现
private Animal animal;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public int getAge() {
return age;
}
// 必须使用@XmlElementRef来标注父类,起到引用的作用
@XmlElementRef
public Animal getAnimal() {
return animal;
}
}
@Setter
abstract static class Animal {
@XmlElement(name = "Country")
private String country;
@XmlTransient
public String getCountry() {
return country;
}
}
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@XmlRootElement(name = "Cat")
static class Cat extends Animal {
@XmlElement(name = "CatName")
private String name;
@XmlElement(name = "Hobby")
private String hobby;
@XmlTransient
public String getName() {
return name;
}
@XmlTransient
public String getHobby() {
return hobby;
}
}
}