Java 原生 JAXB 解析 XML 深入剖析

目录

JAXB 简 介

Marshaller 编组/序列化

入门示例

多注解使用

一对多序列化

格式化处理

Unmarshaller 解组/反序列化

简单对象

关联对象反序列化

xml文档数据的修改

JDK1.7 JAXB 快捷转换

序列化

反序列化


JAXB 简 介

1、JAXB(Java Architecture for XML Binding) 提供了Java 对象与 XML 文本之间互相转换的功能。

2、Java 自身为处理 XML 文件和结构提供了多种选择,目前应用最为广泛的是 JAXB 工具库,从 JRE6 开始,JAXB 就已经成为了JRE 的内置模块, javax.xml.bind 包下,无需再倒入额外的包。

3、JAXB 为 XML 节点和属性提供了各种面向对象的处理方式,可以基于自定义类型、注解等方式将 XML 转换成 Java 对象。

4、XML(可扩展标记语言)遵循W3C标准,是一种通用的数据交换格式,具有很强的跨平台性,如果数据需要做跨平台传输,那么把数据保存在 XML 文件中是个不错的选择。XML 仅仅是作为一种文档模式的结构化存储,所以并不适用于大数据量的存储。

1)常用 API

javax.xml.bind.JAXBContext (类) XML 解析的入口,提供了管理实现 JAXB 绑定框架操作所需的 XML/Java 绑定信息的抽象,包括:解组、编组和验证。
javax.xml.bind.Marshaller (接口) 将 Java 内容树序列化为 XML 数据的,称为 编组.
javax.xml.bind.Unmarshaller (接口) 将 XML 数据反序列化为 Java 内容树,称为 解组。可在解组时有选择地验证 XML 数据。针对各种不同的输入种类提供各种重载的 unmarshal 方法。

2)常用注解

@XmlRootElement

将 Java 类或枚举映射成 XML 根节点元素,其 name 属性指定根节点名称,不指定默认为类名的小写;

这是唯一一个必须要写的注解,其余注解不写时都有默认处理方式,而 @XmlRootElement 则必须标注,否则抛异常:com.sun.istack.internal.SAXException2: 由于类型 "xxx" 缺少 @XmlRootElement 注释, 无法将该类型编集为元素。写在类上。

@XmlElement 将Java类的属性映射为XML节点元素,其 name 属性可自定义元素名;写在属性的getter或者setter方法上。
@XmlAttribute

将Java类的一个属性映射为XML节点元素的属性,其name属性可自定义属性名;写在属性的getter或者setter方法上。

@XmlType

将Java类或枚举类型映射到XML模式类型,常与@XmlRootElement、@XmlAccessorType共用,其propOrder属性定义属性生成的XML节点顺序;写在类上。
@XmlTransient 用于标识在由Java对象映射XML时,忽略此属性,则生成的XML文件中将不出现此元素。写在属性的getter或者setter方法上。
@XmlAccessOrder

用于控制 JAXB 绑定类中属性和字段的排序。AccessorOrder.ALPHABETICAL—对生成的XML元素按字母顺序排序,XmlAccessOrder.UNDEFINED——不排序(默认值)。如果想指定更细粒度的排序,请使用@XmlType。写在类上。

@XmlElementWrapper 对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。写在属性的getter或者setter方法上。
@XmlJavaTypeAdapter 自定义适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),解决日期(Date),数字(Number)格式化问题;写在属性的getter或者setter方法上。

Marshaller 编组/序列化

入门示例

如下所示准备的 POJO 对象 User 如下,此时只写了必须要添加的 @XmlRootElement 注解在类上。

import javax.xml.bind.annotation.XmlRootElement;
import java.util.Date;

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射
 * 除此之外的其余注解可写可不写
 */
@XmlRootElement
public class User {
    /**
     * uID:用户编号
     * uName:用户姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:用户生日
     */
    private Integer uID;
    private String uName;
    private Date birthday;
    private Boolean sex;

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public Integer getuID() {
        return uID;
    }

    public void setuID(Integer uID) {
        this.uID = uID;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    @Override
    public String toString() {
        return "User{" +
                "birthday=" + birthday +
                ", uID=" + uID +
                ", uName='" + uName + '\'' +
                ", sex=" + sex +
                '}';
    }
}

POJO 到 XML 的转换非常简单,3行核心代码即可搞定:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.util.Date;
import java.util.Random;
import java.util.UUID;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    public static void main(String[] args) {
        try {
            /** 准备进行序列化的 POJO 对象数据*/
            User user = new User();
            user.setuID(new Random().nextInt(10000));
            user.setuName("奋六世之余烈");
            user.setBirthday(new Date());
            user.setSex(true);

            /**
             * 1、根据 User 类创建 jaxb 新上下文
             * JAXBContext 类中重载了多个newInstance方法,其中 -
             * public static JAXBContext newInstance( Class... classesToBeBound )是比较常用的一个
             */
            JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
            /**
             * 2、创建一个可以用来将 java 内容树转换为 XML 数据的 Marshaller 对象
             * JAXB_FORMATTED_OUTPUT:指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称,为 false 或者不写时,编组的结果会是压缩的一行
             * Marshaller.JAXB_ENCODING:指定 xml 文件的编码,默认为 UTF-8
             * Marshaller.JAXB_FRAGMENT:指定是否省略 xml 文件的头信息<?xml version="1.0" encoding="utf-8"?>,默认或者为false都是显示
             */
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);

            /**
             * 3、将以 user 为根的内容树编组到输出流中。
             * Marshaller 中重载了多个 marshal 方法,其中比较常用的是-
             * marshal( Object jaxbElement, java.io.OutputStream os )
             * marshal( Object jaxbElement, File output )
             * marshal( Object jaxbElement, java.io.Writer writer )
             */
            marshaller.marshal(user, System.out);//直接输出到控制台中
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

运行结果如上,如果想要指定生成的 xml 内容<user>下的元素顺序,则可以在类上指定 @XmlType 注解,使用其 propOrder 属性。

public abstract String[] propOrder :在将类映射到 XML 模式复杂类型时,指定 XML 模式元素的顺序。 

修改  User 类内容如下,其余内容不变:

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射
 * 除此之外的其余注解可写可不写
 * @XmlType propOrder :在将类映射到 XML 模式复杂类型时,指定 XML 模式元素的顺序。默认情况下 propOrder 中的元素名称与个数都必须与类中的一致,只能是顺序不同。
 */
@XmlType(propOrder = {"uID", "uName", "birthday", "sex"})
@XmlRootElement
public class User {

运行结果如下,此时 <user>下的元素顺序与 @XmlType 中 propOrder 指定的一致。

多注解使用

这里将介绍  @XmlType、@XmlRootElement、@XmlElement、@XmlTransient 的使用,将 POJO 转换好的 xml 内容直接输出到本地文件中。

POJO 对象 User 内容修改如下:

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import java.util.Date;

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射,除此之外的其余注解可写可不写。-
 * 其 name 属性可以指定 xml 内容根节点元素的名称,默认为类名首字母小写
 * @XmlType propOrder :在将类映射到 XML 模式复杂类型时,指定 XML 模式元素的顺序。默认情况下 propOrder 中的元素名称与个数都必须与类中的一致,只能是顺序不同。
 * 如果某属性使用 @XmlTransient 排除不进行序列化时,propOrder 属性中也不能再写。
 */
@XmlType(propOrder = {"uName", "birthday", "sex"})
@XmlRootElement(name = "com.lct.jaxb.User")
public class User {
    /**
     * uID:用户编号
     * uName:用户姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:用户生日
     */
    private Integer uID;
    private String uName;
    private Date birthday;
    private Boolean sex;

    public Date getBirthday() {
        return birthday;
    }

    /**
     * @param birthday
     * @XmlElement :可以写在属性的 setter、getter 方法上,不要直接写在属性上-
     * <p></p>name属性值指定xml中的节点元素名,默认为类的属性名称-
     * 当类中的属性未标识 @XmlElement 注解时,则默认会进行 xml 内容映射-
     * 如果想不让某个属性进行 xml 元素节点映射,则标识 @XmlTransient 注解
     */
    @XmlElement(name = "user_birthday")
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @XmlElement(name = "user_sex")
    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    /**
     * 如果想不让某个属性进行 xml 元素节点映射,则标识 @XmlTransient 注解,此时生成的xnl内容中不会此属性元素
     *
     * @XmlTransient 同样可以写在setter、getter 方法上,不要直接写在属性上
     * 标识了 @XmlTransient 后,@XmlType(propOrder = {"x"...}) 中则不能再有顺序排列,否则报错
     */
    @XmlTransient
    public Integer getuID() {
        return uID;
    }

    public void setuID(Integer uID) {
        this.uID = uID;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    @Override
    public String toString() {
        return "User{" +
                "birthday=" + birthday +
                ", uID=" + uID +
                ", uName='" + uName + '\'' +
                ", sex=" + sex +
                '}';
    }
}

JAXB 转换代码修改如下:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.util.Date;
import java.util.Random;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    /**
     * 日志记录器
     */
    public static final Logger logger = Logger.getGlobal();

    public static void main(String[] args) {
        try {
            /** 准备进行序列化的 POJO 对象数据
             * 如果对象某个属性值为 null,则默认情况下,序列化的 xml 结果中不会有此属性元素*/
            User user = new User();
            user.setuID(new Random().nextInt(10000));
            user.setuName("奋六世之余烈");
            user.setBirthday(new Date());
            user.setSex(null);//为 null 或者未设值时,xml 内容中不会有此属性元素

            /**
             * 1、根据 User 类创建 jaxb 新上下文
             * JAXBContext 类中重载了多个newInstance方法,其中 -
             * public static JAXBContext newInstance( Class... classesToBeBound )是比较常用的一个
             */
            JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
            /**
             * 2、创建一个可以用来将 java 内容树转换为 XML 数据的 Marshaller 对象
             * JAXB_FORMATTED_OUTPUT:指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称,为 false 或者不写时,编组的结果会是压缩的一行
             * Marshaller.JAXB_ENCODING:指定 xml 文件的编码,默认为 UTF-8
             * Marshaller.JAXB_FRAGMENT:指定是否省略 xml 文件的头信息<?xml version="1.0" encoding="utf-8"?>,默认或者为false都是显示
             */
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);

            /**
             * 3、将以 user 为根的内容树编组到输出流中。
             * Marshaller 中重载了多个 marshal 方法,其中比较常用的是-
             * marshal( Object jaxbElement, File output )
             * marshal( Object jaxbElement, java.io.OutputStream os )
             * marshal( Object jaxbElement, java.io.Writer writer )
             */
            //marshaller.marshal(user, System.out);//直接输出到控制台中

            /**marshal( Object jaxbElement, File output )
             * 直接将序列化的xml内容保存到磁盘文件中
             * output 文件不存在时会自动生成,其父目录必须存在。
             * output 如果已经存在内容,则会进行覆盖
             */
            marshaller.marshal(user, new File("E:/wmx/test/1.xml"));
            logger.info("POJO 2 Xml 转换完成...");
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

如上所示:birthday 属性被修改为 user_birthday,uID 属性因为被修饰为 @XmlTransient,所以结果没有,sex 属性因为被设置为 null,所以结果页没有。

一对多序列化

一个用户(User)可以有多本书(Book),这里演示关联序列化,其实也很简单,Book 与 User 写法完全一样。

这里附带会将介绍 @XmlAccessorOrder、@XmlAttribute 注解的使用。

Book 实体内容如下:

import javax.xml.bind.annotation.XmlAccessOrder;
import javax.xml.bind.annotation.XmlAccessorOrder;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * Created by Administrator on 2019/2/14 0014.
 * 书籍实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 根元素,必须要写的注解
 * @XmlAccessorOrder :控制 JAXB 绑定类中属性和字段的排序。ALPHABETICAL:类中的字段和属性是按照字母顺序进行排列;UNDEFINED——不排序(默认值)
 */
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
@XmlRootElement
public class Book {
    private Integer bID;
    private String bName;
    private Float price;

    public Integer getbID() {
        return bID;
    }

    /**
     * @XmlAttribute: 将Java类的一个属性映射为XML节点元素的属性,其name属性可自定义属性名.
     * 可以写在 setter、getter方法上,不要直接写在属性上.
     * 结果如:<books bID="862">
     */
    @XmlAttribute
    public void setbID(Integer bID) {
        this.bID = bID;
    }

    public String getbName() {
        return bName;
    }

    public void setbName(String bName) {
        this.bName = bName;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bID=" + bID +
                ", bName='" + bName + '\'' +
                ", price=" + price +
                '}';
    }
}

User实体内容如下:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射,除此之外的其余注解可写可不写。-
 * 其 name 属性可以指定 xml 内容根节点元素的名称,默认为类名首字母小写
 * @XmlType propOrder :在将类映射到 XML 模式复杂类型时,指定 XML 模式元素的顺序。默认情况下 propOrder 中的元素名称与个数都必须与类中的一致,只能是顺序不同。
 * 如果某属性使用 @XmlTransient 排除不进行序列化时,propOrder 属性中也不能再写。
 */
@XmlType(propOrder = {"uID", "uName", "birthday", "sex", "books"})
@XmlRootElement
public class User {
    /**
     * uID:用户编号
     * uName:用户姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:用户生日
     */
    private Integer uID;
    private String uName;
    private Date birthday;
    private Boolean sex;
    /**
     * 多本书,同理也可以是 Set、Map、Array等
     * 或者单个的 Book,如 private Book book;也是同理
     */
    private List<Book> books;

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public Integer getuID() {
        return uID;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    public void setuID(Integer uID) {
        this.uID = uID;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }
}

POJO 转 xml 编组如下:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    /**
     * 日志记录器
     */
    public static final Logger logger = Logger.getGlobal();

    public static void main(String[] args) {
        try {
            //书籍对象列表
            List<Book> books = new ArrayList<Book>();
            for (int i = 0; i < 2; i++) {
                Book book = new Book();
                book.setbID(new Random().nextInt(1000));
                book.setbName("六脉神剑-" + (i + 1));
                book.setPrice(888.88F);
                books.add(book);
            }
            /** 准备进行序列化的 POJO 对象数据
             * 如果对象某个属性值为 null,则默认情况下,序列化的 xml 结果中不会有此属性元素*/
            User user = new User();
            user.setuID(new Random().nextInt(10000));
            user.setuName("奋六世之余烈");
            user.setBirthday(new Date());
            user.setSex(true);//为 null 或者未设值时,xml 内容中不会有此属性元素
            user.setBooks(books);
            /**
             * 1、根据 User 类创建 jaxb 新上下文
             * JAXBContext 类中重载了多个newInstance方法,其中 -
             * public static JAXBContext newInstance( Class... classesToBeBound )是比较常用的一个
             */
            JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
            /**
             * 2、创建一个可以用来将 java 内容树转换为 XML 数据的 Marshaller 对象
             * JAXB_FORMATTED_OUTPUT:指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称,为 false 或者不写时,编组的结果会是压缩的一行
             * Marshaller.JAXB_ENCODING:指定 xml 文件的编码,默认为 UTF-8
             * Marshaller.JAXB_FRAGMENT:指定是否省略 xml 文件的头信息<?xml version="1.0" encoding="utf-8"?>,默认或者为false都是显示
             */
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
            /**
             * 3、将以 user 为根的内容树编组到输出流中。
             * Marshaller 中重载了多个 marshal 方法,其中比较常用的是-
             * marshal( Object jaxbElement, File output )
             * marshal( Object jaxbElement, java.io.OutputStream os )
             * marshal( Object jaxbElement, java.io.Writer writer )
             */
            marshaller.marshal(user, System.out);//直接输出到控制台中
            /**marshal( Object jaxbElement, File output )
             * 直接将序列化的xml内容保存到磁盘文件中
             * output 文件不存在时会自动生成,其父目录必须存在。
             * output 如果已经存在内容,则会进行覆盖
             */
            marshaller.marshal(user, new File("E:/wmx/test/2.xml"));
            logger.info("POJO 2 Xml 转换完成...");
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下,可以看到此时 book 中的子元素是字典顺序进行排列的:

可以看到 list 序列化的结果中,<books>元素名称默认使用的类的属性名称,视觉上没有层次结构,如果想要修改,可以使用集合包装器注解 @XmlElementWrapper 自定义一个包装节点,这样产生的XML文档才更有层次。

在 User 类中的修改如下:

    /**
     * @XmlElement(name = "book") :将xml内容中默认的<books>元素改为 <book>
     * @XmlElementWrapper(name = "bookList") :对所有的 <book>元素外面包裹一个<bookList>元素
     * @param books
     */
    @XmlElementWrapper(name = "bookList")
    @XmlElement(name = "book")
    public void setBooks(List<Book> books) {
        this.books = books;
    }

此时运行结果输出如下:

格式化处理

业务数据中日期、数值通常是必不可少的,在数据存储的时候,如过需要对日期、数值进行格式化,则需要继承适配器抽象类XmlAdapter,并覆写其序列化和反序列化的方法。

注意:如果序列化的时候使用了适配器格式化,则反序列的时候也必须使用适配器格式化。

下面以常用的日期格式化为例,日期格式化处理类如下:

import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by Administrator on 2019/2/14 0014.
 * 自定义类继承适配器抽象类 public abstract class XmlAdapter<ValueType,BoundType>
 */
public class DateXmlAdapte extends XmlAdapter<String, Date> {
    /**
     * 解组/反序列化
     */
    @Override
    public Date unmarshal(String v) throws Exception {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.parse(v);
    }

    /**
     * 编组/序列化
     */
    @Override
    public String marshal(Date v) throws Exception {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.format(v);
    }
}

User 实体如下,将适配器通过 @XmlJavaTypeAdapter 注解应用到需要格式化的时间字段上:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.Date;

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射
 * 除此之外的其余注解可写可不写
 */
@XmlType(propOrder = {"uID", "uName", "birthday", "sex"})
@XmlRootElement
public class User {
    /**
     * uID:用户编号
     * uName:用户姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:用户生日
     */
    private Integer uID;
    private String uName;
    private Date birthday;
    private Boolean sex;

    public Date getBirthday() {
        return birthday;
    }

    @XmlJavaTypeAdapter(DateXmlAdapte.class)
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public Integer getuID() {
        return uID;
    }

    public void setuID(Integer uID) {
        this.uID = uID;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }
}

POJO 转 XML 的代码就不反复粘贴了,与上面的都是一样的,运行结果如下:

Unmarshaller 解组/反序列化

反序列化与序列化操作完全同理,所以这里将只做简单的演示,注意事项如下:

1、序列化时,如果日期等属性进行了适配器格式化,则反序列化的时候也必须使用适配器格式化,否则属性值会为null。

2、序列化时如果某个属性值未设值,或者为null,则默认xml中不会有此元素,反序列化时属性值也为null。

3、序列化与反序列时的 POJO 内容尽量保持一致,如不能序列化后又将 POJO 对象的注解进行了修改,此时反序列化时极可能出错。

4、如果使用注解的 name 属性修改了默认的元素标签名,则反序列化时也必须与序列化时保持一致,否则会找不到,而使属性值为null。

5、反序列化时即使手动删除了头信息 <?xml version="1.0" encoding="utf-8" standalone="yes"?>也是可以正确执行的。

简单对象

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<user>
    <birthday>2019-02-14T13:40:12.506+08:00</birthday>
    <sex>true</sex>
    <uID>1277</uID>
    <uName>奋六世之余烈</uName>
</user>

序列化 POJO 内容如上,现在来进行反序列化为 POJO 对象:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    public static void main(String[] args) {
        try {

            /**
             * 1、根据 User 类创建 jaxb 新上下文
             * JAXBContext 类中重载了多个newInstance方法,其中 -
             * public static JAXBContext newInstance( Class... classesToBeBound )是比较常用的一个
             */
            JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
            /**
             * 2、创建一个可以用来将 XML 数据转换为 java 内容树的 Unmarshaller 对象。
             */
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            /**
             * 3、将指定的 XML 数据反序列化为 Java Bean 内容树,重载了多个 unmarshal 方法,常用的如下:
             * Object unmarshal( java.io.File f )
             * Object unmarshal( java.io.InputStream is )
             * Object unmarshal( java.net.URL url )
             * Object unmarshal( Reader reader )
             */
            User user = (User) unmarshaller.unmarshal(new File("E:/wmx/test/1.xml"));
            System.out.println(user);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

关联对象反序列化

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<user>
    <uID>8462</uID>
    <uName>奋六世之余烈</uName>
    <birthday>2019-02-14T13:35:39.592+08:00</birthday>
    <sex>true</sex>
    <bookList>
        <book bID="651">
            <bName>六脉神剑-1</bName>
            <price>888.88</price>
        </book>
        <book bID="902">
            <bName>六脉神剑-2</bName>
            <price>888.88</price>
        </book>
    </bookList>
</user>

如上所示是之前序列化时写入的内容,编码其实很简单,只要反序列与序列化的时候,保持 POJO 内容一致,则不会出错。

反序列化的代码完全一样,这里不再重复粘贴,运行结果如下:

xml文档数据的修改

对于 JAXB 与 XStream 此类直接将 Java Bean 与 XML 进行互相映射的库,对于数据的修改通常采用的都是先将 xml 文本内容全部读取到内存,修改完成后再写回去文本的方式。

说白了就是 "先读取整个 xml 文件内容,然后进行修改,修改完成后再进行写入",因为就是普通的先读再写,故不再进行演示。

JDK1.7 JAXB 快捷转换

从 jdk1.7 开始,直接新增了一个 API 叫 javax.xml.bind.JAXB ,JAXB 对解组和编组的方法进行了更简单的封装,无需再创建JAXBContext 实例,直接通过 JAXB 静态调用相应的工具方法即可。

Modifier and Type Method and Description
static void marshal(Object jaxbObject, File xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, OutputStream xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, Result xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, String xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, URI xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, URL xml)

将Java对象树写入XML并将其存储到指定的位置。

static void marshal(Object jaxbObject, Writer xml)

将Java对象树写入XML并将其存储到指定的位置。

static <T> T unmarshal(File xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(InputStream xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(Reader xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(Source xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(String xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(URI xml, <T> type)

从给定的XML输入中读取Java对象树。

static <T> T unmarshal(URL xml, <T> type)

从给定的XML输入中读取Java对象树。

有了 public final class JAXB extends Object 的这些静态方法,则一步即可搞定 Java POJO 与 XML 的转换,下面简单的演示几项。

序列化

User 对象内容如下:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.Date;

/**
 * Created by Administrator on 2019/2/13 0013.
 * User 实体
 *
 * @XmlRootElement 将类或枚举类型映射到 XML 元素,这个注解必须进行显示标注,表示此类/枚举与xml进行映射
 * 除此之外的其余注解可写可不写
 */
@XmlType(propOrder = {"uID", "uName", "birthday", "sex"})
@XmlRootElement
public class User {
    /**
     * uID:用户编号
     * uName:用户姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:用户生日
     */
    private Integer uID;
    private String uName;
    private Date birthday;
    private Boolean sex;

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public Integer getuID() {
        return uID;
    }

    public void setuID(Integer uID) {
        this.uID = uID;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    @Override
    public String toString() {
        return "User{" +
                "birthday=" + birthday +
                ", uID=" + uID +
                ", uName='" + uName + '\'' +
                ", sex=" + sex +
                '}';
    }
}

POJO 转 XML JAXB 方式如下:

import javax.xml.bind.JAXB;
import java.io.File;
import java.util.Date;
import java.util.Random;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    public static void main(String[] args) {
        /** 准备进行序列化的 POJO 对象数据*/
        User user = new User();
        user.setuID(new Random().nextInt(10000));
        user.setuName("及至始皇");
        user.setBirthday(new Date());
        user.setSex(true);

        JAXB.marshal(user, new File("E:/wmx/test/4.xml"));//一步搞定,无需再使用 JAXBContext
        JAXB.marshal(user, System.out);//输出到控制台看一看
    }
}

运行结果如下:

反序列化

这里直接反序列化上面生成好的 4.xml 文件。

import javax.xml.bind.JAXB;
import java.io.File;

/**
 * Created by Administrator on 2019/2/14 0014.
 */
public class JaxbStudy {
    public static void main(String[] args) {
        //反序列化
        User user = JAXB.unmarshal(new File("E:/wmx/test/4.xml"), User.class);
        System.out.println(user);
    }
}

运行结果如下:

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/87248660