JAXB 开发详解

课程简介

JAXB(Java Architecture for XML Binding)是 Java 提供的一套 Java 与 XML 相互转换的标准,是工作中基于 XML 开发的利器。

本课程将介绍 JAXB 在处理 XML 与 Java 对象相互转换与传统的基于 DOM 操作时对比的优势,然后系统的介绍 JAXB 相关的注解等核心内容。同时将介绍一些利用 JAXB 在处理 XML 与 Java 对象相互转换过程中的一些难点的处理方式,如动态节点名的生成、类的继承,尤其是类的继承后还需要使用不同的节点名称的场景,以及 XML 与 Java Map 的相互转换等。

认真阅读完本课程内容后,会更加深入地了解 JAXB,并能通过它熟练的解决在工作中遇到的各种 XML 与 Java 对象相互转换的问题。

作者介绍

张翼麟,深圳某互联网公司资深开发工程师。一直从事互联网金融行业,曾做过网银、财务共享、互联网金融等大型项目,喜欢研究技术,擅长基于 XML 和 JSON 的接口设计、多线程编程等,会利用闲暇时间写些博客,其地址:elim.iteye.com

课程内容
第01课:JAXB 初体验

摘要:本内容主要通过对比使用 JAXB 和非 JAXB 进行 Java 对象转 XML、XML 转 Java 对象的方式来介绍 JAXB 的基本功能,让读者对 JAXB 有一个初步体验。

考虑有如下这样两个 class、Person 和 Address,其中 Person 持有一个 Address 的引用。

public class Person {    private Integer id;    private String name;    private Integer age;    private Address address;    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;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }}public class Address {    private Integer id;    private String province;    private String city;    private String area;    private String other;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getProvince() {        return province;    }    public void setProvince(String province) {        this.province = province;    }    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    public String getArea() {        return area;    }    public void setArea(String area) {        this.area = area;    }    public String getOther() {        return other;    }    public void setOther(String other) {        this.other = other;    }}
Java 对象转 XML

假设现在需要把一个 Person 类型的对象生成如下格式的 XML:

<person id="1" name="张三">    <address id="1">        <area>南山区</area>        <city>深圳市</city>        <other>其它</other>        <province>广东省</province>    </address>    <age>30</age></person>
基于 Dom 实现

在没有使用 JAXB 之前,比如使用 Dom 生成 XML 的方式,得如下编程:

@Testpublic void geneByDom() throws Exception {    Person person = this.buildPerson();    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();      DocumentBuilder db = factory.newDocumentBuilder();      Document document = db.newDocument();      Element personEle = document.createElement("person");      personEle.setAttribute("id", person.getId().toString());    personEle.setAttribute("name", person.getName());    Element ageEle = document.createElement("age");    ageEle.setTextContent(person.getAge().toString());    personEle.appendChild(ageEle);    Address address = person.getAddress();    Element addressEle = document.createElement("address");    addressEle.setAttribute("id", address.getId().toString());    Element province = document.createElement("province");    province.setTextContent(address.getProvince());    addressEle.appendChild(province);    Element city = document.createElement("city");    city.setTextContent(address.getCity());    addressEle.appendChild(city);    Element area = document.createElement("area");    area.setTextContent(address.getArea());    addressEle.appendChild(area);    Element other = document.createElement("other");    other.setTextContent(address.getOther());    addressEle.appendChild(other);    personEle.appendChild(addressEle);    document.appendChild(personEle);      TransformerFactory transformerFactory = TransformerFactory.newInstance();      Transformer transformer = transformerFactory.newTransformer();      Source xmlSource = new DOMSource(document);      //把生成的XML输出到控制台     Result outputTarget = new StreamResult(System.out);      transformer.transform(xmlSource, outputTarget);  }
基于 JAXB 实现

基于 JAXB 实现时需要通过 JAXB 提供的注解来控制它的一些行为,比如下面通过在 Person 类上 @XmlRootElement 指定它为一个根节点,对应的根节点名称将默认取类名称,然后首字母小写,在本示例中即取 “person”。通过 @XmlAttribute 指定 id 和 name 作为节点 person 的 XML 属性,可以通过 XmlAttribute 的 name 属性指定生成的 XML 属性的名称,如 getId() 方法上的 @XmlAttribute(name="id");没有指定 name 属性时对应的 XML 属性名称将使用 Java 属性名称,如 getName() 方法上的 @XmlAttribute。

@XmlRootElementpublic class Person {    private Integer id;    private String name;    private Integer age;    private Address address;    @XmlAttribute(name = "id")    public Integer getId() {       return id;    }    public void setId(Integer id) {        this.id = id;    }    @XmlAttribute    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }}public class Address {    private Integer id;    private String province;    private String city;    private String area;    private String other;    @XmlAttribute(name = "id")    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getProvince() {        return province;    }    public void setProvince(String province) {        this.province = province;    }    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    public String getArea() {        return area;    }    public void setArea(String area) {        this.area = area;    }    public String getOther() {        return other;    }    public void setOther(String other) {        this.other = other;    }}

想要生成目标格式的 XML,基于 Class 的配置就好了。接下来就是调用对应的 API 进行 XML 的转换了。通过如下几行代码就可以把一个 Person 对象生成需要的 XML 格式了。如果还有其他的类型的对象也需要生成 XML,我们只需要把下面代码中的对象和对应的类型更换一下即可,而同样的需求在基于 Dom 的转换中又得重新写一遍代码了,另外从代码层面可以明显的看出基于 JAXB 的方式比基于 Dom 的方式要节约很多代码,实现起来也要简单很多,它的 XML 绑定行为都是通过对应的注解来完成的,非常方便。

@Testpublic void testMarshal() throws JAXBException {    JAXBContext context = JAXBContext.newInstance(Person.class);    Marshaller marshaller = context.createMarshaller();    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);    StringWriter writer = new StringWriter();    //构造Person对象,不是本文的重点    Person person = this.buildPerson();    marshaller.marshal(person, writer);    System.out.println(writer.toString());}

JAXB(Java Architecture for XML Binding)是一项可以通过 XML 产生 Java 对象,也可以通过 Java 对象产生 XML 的技术,是 JDK 自带的功能。JDK 中关于 JAXB 部分有几个比较重要的接口或类,如:

  • JAXBContext:它是程序的入口类,提供了 XML/Java 绑定的操作,包括创建 Marshaller 和 Unmarshaller 等。
  • Marshaller:它负责把 Java 对象序列化为对应的 XML。
  • Unmarshaller:它负责把 XML 反序列化为对应的 Java 对象。

使用 JAXB 进行对象转 XML 的基本操作步骤如下:

//1、获取一个基于某个 class 的 JAXBContext,即 JAXB 上下文JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());//2、利用 JAXBContext 对象创建对应的 Marshaller 实例Marshaller marshaller = jaxbContext.createMarshaller();//3、设置一些序列化时需要的指定的配置marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);StringWriter writer = new StringWriter();//4、将对象进行序列化marshaller.marshal(obj, writer);
  • 创建一个 JAXB 上下文对象,可以传递序列化时需要用到的一些 Class,以便 JAXB 在序列化对应的对象时能够识别其中关联的 Class。
  • 利用 JAXB 上下文对象创建对应的 Marshaller 对象。
  • 指定序列化时的配置参数,具体可以设置的参数和对应的参数的含义可以参考 API 文档。
  • 最后一步是将对应的对象序列化到一个 Writer、OutputStream、File 等输出对象中。

Marshaller 接口中定义了很多 marshal 方法,可以满足我们不同的需要,具体方法定义如下,详情可参考对应的 API 文档。

public void marshal(Object jaxbElement, javax.xml.transform.Result result) throws JAXBException;public void marshal(Object jaxbElement, java.io.OutputStream os) throws JAXBException;public void marshal(Object jaxbElement, File output) throws JAXBException;public void marshal(Object jaxbElement, java.io.Writer writer) throws JAXBException;public void marshal(Object jaxbElement, org.xml.sax.ContentHandler handler) throws JAXBException;public void marshal(Object jaxbElement, org.w3c.dom.Node node) throws JAXBException;public void marshal(Object jaxbElement, javax.xml.stream.XMLStreamWriter writer) throws JAXBException;public void marshal(Object jaxbElement, javax.xml.stream.XMLEventWriter writer) throws JAXBException;

产生了 Marshaller 后,我们可以通过其setProperty( String name, Object value )方法设置一些属性,以控制 XML 的生成。常用的有如下属性。

  • Marshaller.JAXB_ENCODING:指定生成的 XML 的字符集,并不是头信息上的那个 encoding。
  • Marshaller.JAXB\_FORMATTED\_OUTPUT:指定是否需要对生成的 XML 进行格式化,默认是不格式化的。
  • Marshaller.JAXB_FRAGMENT:指定是否需要生成文档级别的事件,指定对应的属性值为 true 可以不生成 XML 的头信息。

使用 JAXB 进行对象的序列化时对应的对象类型必须是 javax.xml.bind.JAXBElement(JAXBElement 是 JAXB 用来表示 XML 元素的一个对象,其中包含了 XML 元素相关的一些信息)类型,或者是使用了 javax.xml.bind.annotation.XmlRootElement 注解标注的类型。

XmlRootElement 用于标注在 class 上面,表示把一个 class 映射为一个 XML Element 对象,而且通常是对应的 XML 的根元素。与之相配合使用的注解通常还有 XmlElement 和 XmlAttribute 等。XmlElement 注解用于标注在 class 的属性上,用于把一个 class 的属性映射为一个 XML Element 对象。

XmlAttribute 注解用于标注在 class 的属性上,用于把一个 class 的属性映射为其 class 对应的 XML Element 上的一个属性。默认情况下,当一个属性没有使用 XmlElement 标注时(更精确的说是没有使用 JAXB 注解标注时,除了 XmlElement 注解以外,JAXB 还有很多其他的注解)也会被序列化为 XML 元素的一个子元素,元素名会取 Java 属性名,这也是为什么在上面的示例中我们不需要在一些 Java 属性或 get 方法上加 @XmlElement 注解,它们也能转换为 XML 元素的原因。

使用 @XmlElement 通常更多的需求是用于指定 XML 元素的名称,比如在上面的示例中如果我们的 address 属性对应的节点名称不希望取默认的 "address",而是希望取 "addr",那么就可以在对应的 getAddress() 方法上加上 @XmlElement(name="addr"),即通过 XmlElement 的 name 属性来指定生成的 XML 元素的名称。

@XmlRootElementpublic class Person {    private Integer id;    private String name;    private Integer age;    private Address address;    @XmlAttribute(name = "id")    public Integer getId() {       return id;    }    public void setId(Integer id) {        this.id = id;    }    @XmlAttribute    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @XmlElement(name="addr")    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }}

生成的 XML 会是如下这样:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person id="1" name="张三">    <addr id="1">        <area>南山区</area>        <city>深圳市</city>        <other>其它</other>        <province>广东省</province>    </addr>    <age>30</age></person>

既然默认情况下没有使用 JAXB 注解进行标注的属性(包括对应的 get 方法)会自动被映射为 XML 的一个元素,那么问题来了,当 Java 类中有些属性是功能辅助性的,是在生成 XML 时不需要转换为对应的 XML 节点时怎么办呢?

比如上面的 Person 在转换为 XML 时不希望 age 属性被转换为 XML,那么默认情况下(随着后面内容的介绍会发现还有其他方式也可以满足这种需求)我们可以在对应的属性的 get 方法上加上 @XmlTransient 注解,这样在转换 XML 时 JAXB 就会忽略该属性了。需要忽略 age 属性,加上 @XmlTransient 注解后,我们的 Person 类定义会是如下这个样子:

@XmlRootElementpublic class Person {    private Integer id;    private String name;    private Integer age;    private Address address;    @XmlAttribute(name = "id")    public Integer getId() {       return id;    }    public void setId(Integer id) {        this.id = id;    }    @XmlAttribute    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @XmlTransient    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }}

基于它转换出来的 XML 代码如下,可以看到与之前的 XML 相比确实是少了 age 节点。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person id="1" name="张三">    <address id="1">        <area>南山区</area>        <city>深圳市</city>        <other>其它</other>        <province>广东省</province>    </address></person>
XML 转 Java 对象

假设应用的类还是上面的 Person 和 Address,XML 还是那段 XML。现需要基于 XML 转换为对应的 Java 对象,我们再来看看基于非 JAXB 的实现和基于 JAXB 的实现。

基于非 JAXB 实现

基于非 JAXB 的实现还是选择上面的 Dom 方式。代码如下,每个属性都得通过解析 Element 获得,如果对应的 XML 结构是动态变化的,那么解析过程还会更复杂。

@Testpublic void unmarshalByDom() throws Exception {    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();      //step2:获得DocumentBuilder      DocumentBuilder db = factory.newDocumentBuilder();      //step3:把需要解析的xml文件加载到一个document对象中      InputStream source = this.getClass().getClassLoader().getResourceAsStream("jaxb/person.xml");    Document document = db.parse(source);      Element personEle = (Element) document.getChildNodes().item(0);    Person person = new Person();    String id = personEle.getAttribute("id");    person.setId(Integer.parseInt(id));    person.setName(personEle.getAttribute("name"));    String age = personEle.getElementsByTagName("age").item(0).getTextContent();    person.setAge(Integer.parseInt(age));    Element addressEle = (Element) personEle.getElementsByTagName("address").item(0);    Address address = new Address();    person.setAddress(address);    String addressId = addressEle.getAttribute("id");    address.setId(Integer.parseInt(addressId));    String province = addressEle.getElementsByTagName("province").item(0).getTextContent();    address.setProvince(province);    String city = addressEle.getElementsByTagName("city").item(0).getTextContent();    address.setCity(city);    String area = addressEle.getElementsByTagName("area").item(0).getTextContent();    address.setArea(area);    String other = addressEle.getElementsByTagName("other").item(0).getTextContent();    address.setOther(other);    System.out.println(person);}
基于 JAXB 的实现

基于 JAXB 实现时,我们应用的 Java 类和对应的 JAXB 注解与 Java 转 XML 示例中的一致,基于这个配置我们只需要如下代码即可实现 XML 转 Java 对象。

@Testpublic void testUnmarshal() throws Exception {    JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();    InputStream source = this.getClass().getClassLoader().getResourceAsStream("jaxb/person.xml");    Person person = (Person) unmarshaller.unmarshal(source);    System.out.println(person);}

进行 XML 转 Java 的基本步骤如下:

//1.创建一个指定 class 的 JAXB 上下文对象JAXBContext context = JAXBContext.newInstance(Person.class);//2.通过 JAXBContext 对象创建对应的 Unmarshaller 对象Unmarshaller unmarshaller = context.createUnmarshaller();InputStream source = this.getClass().getClassLoader().getResourceAsStream("jaxb/person.xml");//3.调用 Unmarshaller 对象的 unmarshal 方法进行反序列化,接收的参数可以是一个 InputStream、Reader、File 等Person person = (Person) unmarshaller.unmarshal(source);

Unmarshaller 对象中提供了一系列的 unmarshal 重载方法,对应的参数类型可以是 File、InputStream、Reader 等,以满足我们在不同情况下的需要,详情可参考对应的 API 文档。

public Object unmarshal(java.io.File f) throws JAXBException;public Object unmarshal(java.io.InputStream is) throws JAXBException;public Object unmarshal(Reader reader) throws JAXBException;public Object unmarshal(java.net.URL url) throws JAXBException;public Object unmarshal(org.xml.sax.InputSource source) throws JAXBException;public Object unmarshal(org.w3c.dom.Node node) throws JAXBException;public <T> JAXBElement<T> unmarshal(org.w3c.dom.Node node, Class<T> declaredType) throws JAXBException;public Object unmarshal(javax.xml.transform.Source source) throws JAXBException;public <T> JAXBElement<T> unmarshal(javax.xml.transform.Source source, Class<T> declaredType)        throws JAXBException;public Object unmarshal(javax.xml.stream.XMLStreamReader reader) throws JAXBException;public <T> JAXBElement<T> unmarshal(javax.xml.stream.XMLStreamReader reader, Class<T> declaredType)        throws JAXBException;public Object unmarshal(javax.xml.stream.XMLEventReader reader) throws JAXBException;public <T> JAXBElement<T> unmarshal(javax.xml.stream.XMLEventReader reader, Class<T> declaredType)        throws JAXBException;
JAXB 工具类

除了使用 JAXBContext 来创建 Marshaller 和 Unmarshaller 对象以实现 Java 对象和 XML 之间的互转外,Java 还提供了一个工具类 JAXB。JAXB 工具类提供了一系列的静态方法来简化了 Java 对象和 XML 之间的互转,具体的方法定义请参考对应的 API 文档。通过它在进行 XML 和 Java 互转时有时会非常方便,只需要简单的一行代码即可搞定。

@Testpublic void testMarshal1() {    Person person = new Person();    person.setId(1);    person.setName("张三");    person.setAge(30);    Address address = new Address();    address.setId(1);    address.setProvince("广东省");    address.setCity("深圳市");    address.setArea("南山区");    address.setOther("其它");    person.setAddress(address);    JAXB.marshal(person, System.out);}@Testpublic void testUnmarshal1() {    InputStream source = this.getClass().getClassLoader().getResourceAsStream("jaxb/person.xml");    Person person = JAXB.unmarshal(source, Person.class);    System.out.println(person);}

以上就是本文的全部内容——JAXB 的入门介绍。

第02课:JAXB 核心注解介绍
第03课:JAXBContext 介绍及性能优化
第04课:以子类的结构绑定 XML
第05课:处理动态元素或属性
第06课:动态生成 XML 节点名称
第07课:监听器
第08课:XML 与 Map 相互转化
第09课:Java 与 Schema 相互转换

阅读全文: http://gitbook.cn/gitchat/column/5a210d8a39fa666a31fb0984

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值