一、概述 JAXB(Java Architecturefor XML Binding)是一个业界的标准,它是一个处理XML文档的易用框架,即可以根据XML Schema产生Java类的技术。与之前的处理方式(比如DOM解析)相比,它的优势在于能够将XML文档直接与java对象绑定,包括序列化(java ->xml)和反序列化(xml -> java),而DOM解析仅仅能够得到一个string类型的节点树,我们可以把jaxb理解为能够对象化处理XML的工具。 就像ORM能够将数据库记录映射为JAVA对象一样,JAXB将XML元素映射为JAVA对象。
二、常用注解
- @XmlRootElement 类级别注解,顶层元素,对应xml文件中的根节点。例如:
@XmlRootElement //默认转化根节点名称是person,可以通过name属性修改根节点名称
public class Person{
..
}
- @XmlAccessorType 用于指定由java对象生成xml文件时对java对象属性的访问方式;作用于包或者顶层元素上,有四个枚举值:
a、XmlAccessType.FELD:java对象中的所有非static 非 transient 的成员变量;
b、XmlAccessType.PROPERTY:java对象中所有通过getter/setter对方式访问的成员变量;
c、XmlAccessType.NONE:java对象的所有属性都不映射为xml的元素,除非明确的使用@XmlElement或@XmlAttribute修饰;
d、XmlAccessType.PUBLIC_MEMBER:java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量,默认方式。 - @XmlElement 用在java类的属性上,用于将属性映射为xml的子节点,注意:默认情况下,私有属性是不会被映射的,但可以通过该注解映射私有成员变量,例如:
@XmlElement //默认元素节点名称是age,可以通过name属性修改xml节点名称
private String age;
- @XmlAttribute 用于把java对象的属性映射为xml的属性;例如:
@XmlAttribute //默认属性名称是id,可以通过name属性修改xml属性名称
private String id;
-
@XmlAccessorOrder 用于对java对象生成的xml元素进行排序。作用于顶级元素上,它有两个属性值:
a、AccessorOrder.ALPHABETICAL:对生成的xml元素按字母顺序排序;
b、XmlAccessOrder.UNDEFINED:不排序。 -
@XmlType 作用于顶层元素或枚举类上,用它标注的类在映射后的 schema 中中会以一个 XML 复杂数据类型的形式出现。我们可以通过 @XmlType 注解的 name 属性来定制映射的 XML 数据类型的名称,用 propOrder 属性来定制映射后的复杂数据类型的内容顺序等。例如:
@XmlType(name = "SubPerson", propOrder = { "name", "id", "age"})
public class Person{
private int id;
private String name;
private int age;
..
}
这个 Java 类在映射后的 Web 服务 schema 中会表现为:
- @XmlTransient 用于标识在由java对象映射xml时,忽略此属性。即,在生成的xml文件中不出现此元素。
- @XmlJavaTypeAdapter 常用在转换比较复杂的对象时,如map类型或者格式化日期等。使用此注解时,需要自己写一个adapter类继承XmlAdapter抽象类,并实现里面的方法。
@XmlJavaTypeAdapter(value=xxx.class),value为自己定义的adapter类 XmlAdapter定义如下:
public abstract class XmlAdapter {
// Do-nothing constructor for the derived classes.
protected XmlAdapter() {}
// Convert a value type to a bound type.
public abstract BoundType unmarshal(ValueType v);
// Convert a bound type to a value type.
public abstract ValueType marshal(BoundType v);
}
- @XmlAnyAttribute和@XmlAnyElement 任意随机的attribute和element。
- @XmlElementWrapper 生成一个包装 XML表示形式的包装器元素。 此元素主要用于生成一个包装集合的包装器 XML元素。
- @XmlElements 作为一个容器,是一个collection集合,可以包含不同类型,也可以用继承中的子类,详解如下:
@XmlRootElement
public class Person {
@XmlElements({
@XmlElement(name="dog", type=Dog.class),
@XmlElement(name="cat", type=Cat.class),
@XmlElement(name="pig", type=Pig.class)
})
public List animals = Arrays.asList(new Dog(), new Cat(), new Pig());
}
结果是:
big
red
200kg
优化处理,使用@XmlElementWrapper来给集合属性增加一个wrapper:
@XmlRootElement
public class Person {
@XmlElementWrapper(name="animals")
@XmlElements({
@XmlElement(name="dog", type=Dog.class),
@XmlElement(name="cat", type=Cat.class),
@XmlElement(name="pig", type=Pig.class)
})
public List animals = Arrays.asList(new Dog(), new Cat(), new Pig());
}
结果是:
big
red
200kg
其他类定义如下:
//这里不能使用接口, JAXB 无法处理接口
public abstract class Animal {}
public class Cat extends Animal{
public String color = "red";
}
public class Dog extends Animal{
public String size = "big";
}
public class Pig extends Animal{
public String weight = "200kg";
}
//Person类是JAXB根元素,包含一个Animal的集合
@XmlRootElement
public class Person {
public List animals = Arrays.asList(new Dog(), new Cat(), new Pig());
}
三、序列化和反序列化
不管是序列化还是反序列化,都需要获得的就是JAXBContext对象,它提供了JAXB API 的入口,我们使用JAXBContext.newInstance静态方法来获得它,问题在于需要传入的参数。有两种方法:
- 编写XML Schema,然后使用xjc实用工具编译;
- 手动编写pojo,使用特定注解修饰。
通过编写Java代码实现如下(通常情况下,顶层元素就足够了,因为JAXB将会从顶层元素开始递归引用到 顶层元素涉及到的类型(比如实例变量的类型),但是顶层元素的子类是不会被JAXB处理到的):
@XmlRootElement
public class Person {
public String name = "zhangsan";
}
@XmlRootElement
public class Dog {
public String size = "big";
public Person owner = new Person();
}
@XmlRootElement
public class LittleDog extends Dog{
public int age = 11;
}
我们在创建JAXBContext的时候,如果仅仅传入Dog.class,那么Dog和Person会被JAXB处理,而LittleDog则不会,如果我们使用JAXB处理LittleDog,那么它会被当成Dog来处理。
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Dog.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(new Person(), System.out);
marshaller.marshal(new LittleDog(), System.out);
}
结果是:
zhangsan
big
zhangsan
我们除了明确的把LittleDog.class也传入到JAXBContext.newInstance方法中去外,还可以使用@XmlSeeAlso,如下:
@XmlRootElement
//XmlSeeAlso的含义就是说当JAXB处理Dog的时候也处理哪些类型(先显示父类然后是子类)
@XmlSeeAlso({LittleDog.class})
public class Dog {
public String size = "big";
public Person owner = new Person();
}
结果:
big
zhangsan
11