JAXB應用實例

  過往的項目中數據存儲都離不開數據庫,不過最近作的一個項目的某些數據(好比人員信息、菜單、權限等等)卻徹底沒有涉及任何數據庫操做,直接XML搞定。這裏無心比較優劣,由於數據庫存儲和XML存儲本就有不一樣的適用場景,盲目比較毫無心義,只是由於業務須要,僅此而已。先來概念一下——XML,可擴展標記語言,設計宗旨是用來傳輸數據而非顯示數據,其遵循W3C標準,是一種通用的數據交換格式,具備很強的跨平臺性,而且數據無需轉換,因此,若是你要將數據作跨平臺傳輸,那麼把數據保存在 XML 文件中是有好處的。固然,這裏要說明,因爲XML僅僅是做爲一種文檔模式的結構化存儲,因此並不適用於大數據量的存儲。如今的Java中有不少類庫好比DOM、SAX、JDOM和DOM4J等等均可以操做XML,但若是僅僅是想作JavaBean和XML節點元素的互相轉換,而不涉及動態XML的處理,那麼JAXB絕對是一個不錯的選擇。在比較新的jdk版本中,JAXB都是jdk的擴展包javax中自帶的類庫,不須要你引入第三方jar包。下面,博主正式給看客上菜,詳細介紹一下JAXB的實際用法——java

 一 JavaBean和XML相互轉換初體驗

  1 package model;
  2 
  3 import javax.xml.bind.annotation.*;
  4 import java.io.Serializable;
  5 
  6 //JavaBean代碼
  7 
  8 @XmlType(propOrder = {})
  9 @XmlRootElement(name = "user")
 10 @XmlAccessorType(XmlAccessType.PROPERTY)
 11 @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
 12 public class User implements Serializable {
 13 
 14     private String userName;
 15     private int age;
 16     private String role;
 17     private String bibi;
 18 
 19     public User() {
 20     }
 21 
 22     public User(String userName, int age, String role, String bibi) {
 23         this.userName = userName;
 24         this.age = age;
 25         this.role = role;
 26         this.bibi = bibi;
 27     }
 28 
 29     public String getUserName() {
 30         return userName;
 31     }
 32 
 33     public void setUserName(String userName) {
 34         this.userName = userName;
 35     }
 36 
 37     @XmlAttribute
 38     public int getAge() {
 39         return age;
 40     }
 41 
 42     public void setAge(int age) {
 43         this.age = age;
 44     }
 45 
 46     @XmlElement
 47     public String getRole() {
 48         return role;
 49     }
 50 
 51     public void setRole(String role) {
 52         this.role = role;
 53     }
 54 
 55     @XmlTransient
 56     public String getBibi() {
 57         return bibi;
 58     }
 59 
 60     public void setBibi(String bibi) {
 61         this.bibi = bibi;
 62     }
 63 
 64     @Override
 65     public String toString() {
 66         return "User{" +
 67                 "userName='" + userName + '\'' +
 68                 ", age=" + age +
 69                 ", role='" + role + '\'' +
 70                 ", bibi='" + bibi + '\'' +
 71                 '}';
 72     }
 73 }
 74 
 75 //測試
 76 public class test {
 77     @Test
 78     public void saveXmlTest() {
 79         User user = new User("陳本布衣", 2018, "超級管理員","瞎嗶嗶");
 80         File file = new File("E://user.xml");
 81         try {
 82             JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
 83             Marshaller marshaller = jaxbContext.createMarshaller();
 84             //格式化輸出,即按標籤自動換行,不然就是一行輸出
 85             marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 86             //設置編碼(默認編碼就是utf-8)
 87             marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
 88             //是否省略xml頭信息,默認不省略(false)
 89             marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
 90             marshaller.marshal(user, file);
 91         } catch (JAXBException e) {
 92             e.printStackTrace();
 93         }
 94     }
 95 
 96     @Test
 97     public void getUserTest() {
 98         File file = new File("E://user.xml");
 99         try {
100             JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
101             Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
102             User user = (User) unmarshaller.unmarshal(file);
103             System.out.println(user.toString());
104         } catch (JAXBException e) {
105             e.printStackTrace();
106         }
107     }
108 }
View Code

生成的XML:數據庫

二 JAXB使用基礎介紹

  ① 經常使用API

  • JAXBContext類,是應用的入口,經過該類建立序列化和反序列化對象,也即編組對象和解組對象
  • Marshaller 編組接口,將Java對象序列化爲XML數據;
  • Unmarshaller 解組接口,將XML數據反序列化爲Java對象。

  ② 經常使用註解

  • @XmlRootElement,將Java類或枚舉映射成XML元素根節點,是惟一一個必須註解,name屬性指定根節點名稱,不指定默認爲類名的小寫;
  • @XmlElement,將Java類的一個屬性映射爲XML節點元素,name屬性可自定義元素名;
  • @XmlAttribute,將Java類的一個屬性映射爲XML節點元素的屬性,name屬性可自定義屬性名
  • @XmlType,將Java類或枚舉類型映射到XML模式類型,常與@XmlRootElement、@XmlAccessorType共用,propOrder屬性定義字段生成的XML節點順序;
  • @XmlAccessorType,控制字段或屬性的序列化。屬性XmlAccessType有4個常量值:FIELD表示JAXB將自動綁定Java類中的每一個非靜態的(static)、非瞬態的(由@XmlTransient標註)字段到XML;PROPERTY表示java對象中全部經過getter/setter方式綁定成屬性到XML;PUBLIC_MEMBER表示Java對象中全部的public訪問權限的成員變量和經過getter/setter方式訪問的成員變量,該值爲默認值;NONE表示Java對象的全部屬性都不映射爲XML的元素;
  • @XmlAccessorOrder,控制JAXB 綁定類中屬性和字段的排序,有兩個屬性,AccessorOrder.ALPHABETICAL——對生成的XML元素按字母書序排序,XmlAccessOrder.UNDEFINED——不排序,默認爲該值;
  • @XmlJavaTypeAdapter,自定義適配器(即擴展抽象類XmlAdapter並覆蓋marshal()和unmarshal()方法),解決日期(Date),數字(Number)格式化問題;
  • @XmlElementWrapper ,對於數組或集合(即包含多個元素的成員變量),生成一個包裝該數組或集合的XML元素(稱爲包裝器),該註解只能用在集合上;
  • @XmlTransient ,用於標示在由Java對象映射XML時,忽略此屬性,在生成的XML文件中將不出現此元素。

  ③ 實際應用中注意的問題

  ① 若是JavaBean中定義了有參的構造器,那麼必須同時定義無參構造器,不然轉XML會拋無默認構造函數的異常;數組

  ② 成員變量值爲NULL時,將不會映射成對應的XML元素——因爲基本數據類型默認值不爲空,因此基本數據類型不設值也會映射成XML元素,值爲默認值,因此若是模型須要基本數據,在屬性定義的時候儘可能使用包裝類型;
app

  ③ @XmlAccessorType 註解中若是屬性值爲XmlAccessType.FIELD,則表示經過成員變量來映射,set/get方法上的映射註解就是多餘的,因此若是此時set/get方法上再標註元素或者屬性映射註解,將拋屬性重複性異常;屬性值爲XmlAccessType.NONE不映射爲XML元素的前提是Java字段或set/get方法上都沒有映射註解;
dom

  ④ @XmlType propOrder屬性可以自定義字段的排序,該屬性若是設置,要麼寫成{}的形式,不然在就必須將全部@XmlElement標註或者沒有@XmlElement標註的但實際上會被映射爲XML節點的字段添加到排序列表,否則會拋異常;若是propOrder屬性設置有值,@XmlAccessorOrder註解的元素排序規則將失效;ide

三 應用實際

  先準備好測試用的工具方法:函數

 1 package util;
 2 
 3 
 4 import model.User;
 5 
 6 import javax.xml.bind.JAXBContext;
 7 import javax.xml.bind.JAXBException;
 8 import javax.xml.bind.Marshaller;
 9 import javax.xml.bind.Unmarshaller;
10 import java.io.File;
11 
12 public class JaxbUtil {
13 
14     public static void convertToXml(Object obj, File file) {
15         try {
16             JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
17             Marshaller marshaller = jaxbContext.createMarshaller();
18             //格式化輸出,即按標籤自動換行,不然就是一行輸出
19             marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
20             //設置編碼(默認編碼就是utf-8)
21             marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
22             //是否省略xml頭信息,默認不省略(false)
23             marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
24             marshaller.marshal(obj, file);
25             //控制檯輸出
26             marshaller.marshal(obj,System.out);
27         } catch (JAXBException e) {
28             e.printStackTrace();
29         }
30     }
31 
32     public static <T> T convertToJavaBean(Class<T> clz, File file) {
33         try {
34             JAXBContext jaxbContext = JAXBContext.newInstance(clz);
35             Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
36             T t = (T) unmarshaller.unmarshal(file);
37             return t;
38         } catch (JAXBException e) {
39             e.printStackTrace();
40         }
41         return null;
42     }
43 }

  ① 簡單對象處理

  簡單對象處理起來比較簡單,譬如人員對象User中包含菜單Menu,只需將定義的普通Menu對象也按照JAXB的註解進行標註,在User對象中當成普通字段同樣的定義便可——工具

@XmlType(propOrder = {"userName","role","menu"})
@XmlRootElement(name = "user")
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable {

    private String userName;
    private int age;
    private String role;
    private String bibi;
    private Menu menu;
    public User() {
    }

    public User(String userName, int age, String role, String bibi) {
        this.userName = userName;
        this.role = role;
        this.age = age;
        this.bibi = bibi;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @XmlAttribute
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @XmlElement(nillable=true)
    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    @XmlTransient
    public String getBibi() {
        return bibi;
    }

    public void setBibi(String bibi) {
        this.bibi = bibi;
    }

    @XmlElement
    public Menu getMenu() {
        return menu;
    }

    public void setMenu(Menu menu) {
        this.menu = menu;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", role='" + role + '\'' +
                ", menu=" + menu +
                '}';
    }
}


//菜單對象

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Menu {
    private String name;
    private String id;

    public Menu() {
    }

    public Menu(String name, String id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public String toString() {
        return "Menu{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                '}';
    }
}
View Code

 

  ② 集合處理

  實際應用場景中集合應用要更常見一些,好比上面的用戶菜單,一個用戶確定會有多個不一樣的菜單,因此,咱們來將上面的菜單改用集合處理——性能

 1 package model;
 2 
 3 import javax.xml.bind.annotation.*;
 4 import java.io.Serializable;
 5 import java.util.Date;
 6 import java.util.List;
 7 
 8 @XmlType(propOrder = {"userName", "role", "menus"})
 9 @XmlRootElement(name = "user")
10 @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
11 public class User implements Serializable {
12 
13     private String userName;
14     private int age;
15     private String role;
16     private String bibi;
17     private List<Menu> menus;
18 
19     public User() {
20     }
21 
22     public User(String userName, int age, String role, String bibi) {
23         this.userName = userName;
24         this.role = role;
25         this.age = age;
26         this.bibi = bibi;
27     }
28 
29     public String getUserName() {
30         return userName;
31     }
32 
33     public void setUserName(String userName) {
34         this.userName = userName;
35     }
36 
37     @XmlAttribute
38     public int getAge() {
39         return age;
40     }
41 
42     public void setAge(int age) {
43         this.age = age;
44     }
45 
46     @XmlElement
47     public String getRole() {
48         return role;
49     }
50 
51     public void setRole(String role) {
52         this.role = role;
53     }
54 
55     @XmlTransient
56     public String getBibi() {
57         return bibi;
58     }
59 
60     public void setBibi(String bibi) {
61         this.bibi = bibi;
62     }
63 
64     @XmlElement
65     public List<Menu> getMenus() {
66         return menus;
67     }
68 
69     public void setMenus(List<Menu> menus) {
70         this.menus = menus;
71     }
72 
73     @Override
74     public String toString() {
75         return "User{" +
76                 "userName='" + userName + '\'' +
77                 ", age=" + age +
78                 ", role='" + role + '\'' +
79                 ", menus=" + menus +
80                 '}';
81     }
82 }
View Code
 1 package model;
 2 
 3 import javax.xml.bind.annotation.XmlAttribute;
 4 import javax.xml.bind.annotation.XmlRootElement;
 5 import java.util.List;
 6 
 7 @XmlRootElement
 8 public class Menu {
 9     private String name;
10     private String id;
11     private List<Menu> child;
12 
13     public Menu() {
14     }
15 
16     public Menu(String name, String id) {
17         this.name = name;
18         this.id = id;
19     }
20     @XmlAttribute
21     public String getName() {
22         return name;
23     }
24 
25     public void setName(String name) {
26         this.name = name;
27     }
28     @XmlAttribute
29     public String getId() {
30         return id;
31     }
32 
33     public void setId(String id) {
34         this.id = id;
35     }
36 
37     public List<Menu> getChild() {
38         return child;
39     }
40 
41     public void setChild(List<Menu> child) {
42         this.child = child;
43     }
44 
45     @Override
46     public String toString() {
47         return "Menu{" +
48                 "name='" + name + '\'' +
49                 ", id='" + id + '\'' +
50                 '}';
51     }
52 }
View Code
 1 package test;
 2 
 3 
 4 import model.Menu;
 5 import model.User;
 6 import org.junit.Test;
 7 import util.JaxbUtil;
 8 import java.io.File;
 9 import java.util.ArrayList;
10 import java.util.List;
11 
12 public class test {
13     @Test
14     public void saveXmlTest() {
15         User user = new User("陳本布衣", 2018, "超級管理員","瞎嗶嗶");
16         List<Menu> list1 = new ArrayList<>();
17         Menu menu1 = new Menu("系統管理","9527");
18         Menu child1 = new Menu("權限管理","9999");
19         Menu child2 = new Menu("用戶管理","2322");
20         list1.add(child1);
21         list1.add(child2);
22         menu1.setChild(list1);
23         List<Menu> list2 = new ArrayList<>();
24         Menu menu2 = new Menu("參數配置","2222");
25         Menu child3 = new Menu("權限管理","3333");
26         Menu child4 = new Menu("用戶管理","4444");
27         list2.add(child3);
28         list2.add(child4);
29         menu2.setChild(list2);
30         List<Menu> menus = new ArrayList<>();
31         menus.add(menu1);
32         menus.add(menu2);
33         user.setMenus(menus);
34         File file = new File("E://user.xml");
35         JaxbUtil.convertToXml(user,file);
36     }
37 
38     @Test
39     public void getUserTest() {
40         File file = new File("E://user.xml");
41         User user = JaxbUtil.convertToJavaBean(User.class, file);
42         System.out.println(user);
43     }
44 }
View Code

 

  上面的菜單中彷佛少了點層次關係,這個時候可使用集合包裝器註解@XmlElementWrapper自定義一個包裝節點,這樣產生的XML文檔才更有層次:學習

1     @XmlElementWrapper(name = "menu")
2     @XmlElement
3     public List<Menu> getMenus() {
4         return menus;
5     }

  最終產生的XML文檔就是這樣的:

 

  ③ 格式化處理

   業務數據中日期、數值一般是必不可少的,在數據存儲的時候,這些數據一般都須要作格式化處理,好比將日期格式化,貨幣型數值處理等等。JAXB中格式化處理須要繼承適配器抽象類XmlAdapter,並覆寫其序列化和反序列化的方法,這裏僅用經常使用的日期格式化爲例:

 1 package adapter;
 2 
 3 import javax.xml.bind.annotation.adapters.XmlAdapter;
 4 import java.text.DateFormat;
 5 import java.text.SimpleDateFormat;
 6 import java.util.Date;
 7 
 8 public class DateAdapter extends XmlAdapter<String, Date> {
 9     private static final DateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
10 
11     @Override
12     public Date unmarshal(String date) throws Exception {
13         return SDF.parse(date);
14     }
15 
16     @Override
17     public String marshal(Date date) throws Exception {
18         return SDF.format(date);
19     }
20 }

  將該適配器經過註解應用到User類表時間的date字段上:

1     @XmlJavaTypeAdapter(DateAdapter.class)
2     public Date getDate() {
3         return date;
4     }
5 
6     public void setDate(Date date) {
7         this.date = date;
8     }

   最後的時間就是按照格式化輸出——

  

  ④ 數據的修改

  因爲XML是文檔數據類型,對於文檔數據的修改操做,一般採用的都是先將文本內容所有讀取到內存,修改完成後再寫回去文本的方式——雖然Java中有RandomAccessFile類能夠實現對文本任意位置的訪問修改,但博主覺得,在JAXB這種對象模型映射成XML的業務中並不適用。咱們將上面的模型稍微簡化一下,完成根據用戶id修改用戶數據的測試——

@XmlType(propOrder = {"users", "userName", "role", "remark"})
@XmlRootElement(name = "user")
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable {

    private String userName;
    private Integer id;
    private String role;
    private String remark;
    private List<User> users;

    public User() {
    }

    public User(String userName, Integer id, String role, String remark) {
        this.userName = userName;
        this.id = id;
        this.role = role;
        this.remark = remark;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @XmlAttribute
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }
}

//測試代碼
public class test {

    @Test
    public void editUser() {
        User root  = new User();
        File file = new File("E://user.xml");
        //模擬修改後的一條數據
        User editUser = new User("陳本布衣", 2018, "超級管理員","數據修改後");
        User user = JaxbUtil.convertToJavaBean(User.class, file);
        List<User> users = user.getUsers();
        for (int i = 0; i < users.size(); i++) {
            if(users.get(i).getId().equals(editUser.getId())){
                users.set(i,editUser);
            }
        }
        root.setUsers(users);
        JaxbUtil.convertToXml(root,file);
    }
}
View Code

  最後,在XML文檔中你能夠看到文檔內容已經被修改了——

     

四 問題補充

  上述博文中描述的工具方法僅僅是出於學習中追根問本的目的寫得稍微冗餘了些,實際上,我所知道的是最遲從jdk1.7開始,JAXB就對解組和編組的方法進行了更簡單的封裝,因此,實際項目中除非本身要進行個性化設置,不然大可不用本身再建立JAXBContext實例,直接經過JAXB靜態調用相應的工具方法就好了,有興趣的看官稍微跟蹤一下源碼就能瞭然,因而上面的工具方法能夠寫得更簡單——

 1 package util;
 2 
 3 
 4 import javax.xml.bind.JAXB;
 5 import java.io.File;
 6 
 7 public class JaxbUtil {
 8 
 9     public static void convertToXml(Object obj, File file) {
10         JAXB.marshal(obj,file);
11     }
12 
13     public static <T> T convertToJavaBean(Class<T> clz, File file) {
14         return JAXB.unmarshal(file, clz);
15     }
16 }

  OK,對於JAXB的知識分享就差很少這麼些了。對於這種比較單一技能點的學習,就是根據API多寫點代碼練習測試,從測試的結果對錯中總結出本身的深層理解,並在實際項目學以至用,不變應萬變,望看官讀畢都有所收穫!

相關文章
相關標籤/搜索