JAXB是 Java Architecture for XML Binding的首字母缩写,它是一个处理XML文档的易用框架。 与之前的处理方式(比如DOM解析)相比,它的优势在于能够将XML文档直接与java对象绑定,包括序列化(java ->xml)和反序列化(xml -> java),而DOM解析仅仅能够得到一个string类型的节点树,我们可以把jaxb理解为能够对象化处理XML的工具。 就像ORM能够将数据库记录映射为JAVA对象一样,JAXB将XML元素映射为JAVA对象。
我们有两种方式来获得描述节点信息的java类型,一种是写一个XML schema,它们通常是使用 W3C XML Schema Language 写成的(*.xsd),然后使用 jdk的 xjc 实用工具编译这个xsd文件,就可以生成我们需要的一系列java文件了。 第二种方法是我们自己手动编写需要的java文件,其实所需要的都是一些基本的POJO类,我们需要做的只不过是给他们加上JAXB所支持的相应的annotation而已。 这两种方式本质上是一样的,我们手动编写的java文件基本等同于 xjc 实用工具生成的文件。
关于JAXB的第一种方式,不在本文的讨论范围内, 可以参考 JAXB Tutorial ,本文的绝大多数内容也都可以从这个教程中找到。
下面我们展示一个JAXB的简单示例,以期能对JAXB有一个具体的认识。
import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
上面的这个java类实在不能再简单了,唯一值得注意的是它有一个 @XmlRootElement 类注解。下面我们就使用JAXB来对这个POJO类进行XML的序列化(marshal)和反序列化(unmarshal)。
import java.io.FileInputStream; import java.io.FileOutputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class MarshallerTest { public static void main(String[] args) throws JAXBException { marshall(); unMarshal(); } public static void marshall() throws JAXBException{ Person person = new Person(); person.setName("Zhangsan"); person.setAge(11); JAXBContext context = JAXBContext.newInstance(Person.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //将XML打印到控制台 marshaller.marshal(person, System.out); //将XML输出到一个文件,以便后面进行反序列化 try(FileOutputStream out = new FileOutputStream("C:/temp/person.xml")){ marshaller.marshal(person, out); }catch(Exception e){ e.printStackTrace(); } } public static void unMarshal() throws JAXBException{ JAXBContext context = JAXBContext.newInstance(Person.class); Unmarshaller u = context.createUnmarshaller(); try(FileInputStream in = new FileInputStream("C:/temp/person.xml")){ Person person = (Person)u.unmarshal(in); System.out.println(person.getName() + " : " + person.getAge()); }catch(Exception e){ e.printStackTrace(); } } }
生成的XML内容为:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <person> <age>11</age> <name>Zhangsan</name> </person>
序列化和反序列化的步骤
无论序列化还是反序列化第一个需要获得的就是JAXBContext对象,它提供了JAXB API 的入口,我们使用JAXBContext.newInstance静态方法来获得它,问题在于需要传入的参数。
我们在前面提到过使用JAXB有两种方式: 1. 编写XML Schema,然后使用xjc实用工具编译。 2. 手动编写pojo,使用特定注解修饰。 他们获得JAXBContext的方式会稍微不同,我们仅仅考虑第二种情况。
在手动编写java 代码的情况中,我们有两种方式来获得JAXBContext:
1. 传入所有需要被JAXB处理的类的class对象。 通常情况下,顶层元素就足够了,因为JAXB将会从顶层元素开始递归引用到 顶层元素涉及到的类型(比如实例变量的类型),但是顶层元素的子类是不会被JAXB处理到的。
在下面的例子中我们定义了三个pojo:Person 、Dog 和LittleDog, 他们都被XmlRootElement所修饰,其中LittleDog是Dog的子类, 而Person被Dog所引用。
@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); }
上面的序列化结果为:
<!-- 下面是Person对象的序列化结果,因为Person被Dog引用,所以也被JAXB处理了 --> <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <person> <name>zhangsan</name> </person> <!-- 注意下面是LittleDog的序列化结果,很显然它仅仅被当做一个Dog对象处理了--> <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <dog> <size>big</size> <owner> <name>zhangsan</name> </owner> </dog>
我们除了明确的把LittleDog.class也传入到JAXBContext.newInstance方法中去外,还可以使用@XmlSeeAlso,如下:
@XmlRootElement //XmlSeeAlso的含义就是说当JAXB处理Dog的时候也处理哪些类型 @XmlSeeAlso({LittleDog.class}) public class Dog { public String size = "big"; public Person owner = new Person(); }
2. 如果在一个包中定义了一个jaxb.index文件,并在其中声明了需要被JAXB处理的类型,那么我们可以将这个包名传递进来。还是接着上面的例子,我们可以这样定义jaxb.index文件
Dog
LittleDog
这样Dog Person和LittleDog就都可以被JAXB所处理了。这种情况适合于在同一个包中有好多没有引用关系的类的情况。 另外注意 格式为 "ClassName" 或 "OuterClass.InnerClass", 而不是 "ClassName.class" 或 "fully.qualified.ClassName" 。
在获得到JAXBContext之后,我们就可以得到试用createMarshaller和createUnmarshaller分别创建序列化和反序列化对象了。 而真正的序列化和反序列化无非就是将数据写入流中和从流中读取而已。
在下一篇文章中,我们将学习关于JAXB annotation的具体应用。