JAXB注解的使用詳解


前言:

  最近一直在做各種接口的對接,接觸最多的數據類型就是JSON和XML數據,還有XML中包含JSON的數據,而在Java中對象和XML之間的轉換經常用到JAXB注解,抽空在這里總結一下,首先做一下准備工作

  測試類代碼:

@XmlRootElement public class Student { private String name; // 姓名
   private String sex; // 性別
   private int number; // 學號
   private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; } @XmlElement(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "sex") public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @XmlElement(name = "number") public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @XmlElement(name = "className") public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } }
View Code

  工具類代碼:

public class XStreamUtil { /** * 擴展xstream,使其支持CDATA塊 * 整數和浮點數不添加 * @date 2013-05-19 */
    public static XStream xstream2 = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 對所有xml節點的轉換都增加CDATA標記 
                boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { if(!name.equals("xml")){ char[] arr = name.toCharArray(); if (arr[0] >= 'a' && arr[0] <= 'z') { //arr[0] -= 'a' - 'A'; //ASCII碼,大寫字母和小寫字符之間數值上差32
                            arr[0] = (char) ((int) arr[0] - 32); } name = new String(arr);//char數組轉字符串
 } super.startNode(name, clazz); } @Override public void setValue(String text) { if(text!=null && !"".equals(text)){ if(text.matches("[0-9]*(\\.?)[0-9]*") || text.matches("[0-9]*(\\.?)[0-9]*")){//如果是正式或者浮點數
                            cdata = false; }else{ cdata = true; } } super.setValue(text); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); /** * 擴展xstream,使其支持CDATA塊 * * @date 2013-05-19 */
    public static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 對所有xml節點的轉換都增加CDATA標記 
                boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); /** * 將XML內容轉換成對象 */ @SuppressWarnings("unchecked") public static <T> T unmarshal(String xml, Class<T> clazz) throws JAXBException{ JAXBContext jaxbContext = JAXBContext.newInstance(clazz); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); return (T)unmarshaller.unmarshal(new StringReader(xml)); } /** * 將對象轉換成XML */
    public static String marshal(Object object, Class<?> clazz) throws JAXBException{ JAXBContext jaxbContext = JAXBContext.newInstance(clazz); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); StringWriter writer = new StringWriter(); marshaller.marshal(object, writer); return writer.toString(); } /** * 將java對象轉換為xml * @param <T> * @param reqTextMessage * @return
     */
    public static <T> String JavaToXml(T t){ xstream.alias("xml", t.getClass()); return xstream.toXML(t); } }
View Code

  測試代碼:

public class Test { public static void main(String[] args) throws JAXBException { Student st = new Student("張三","男",10001,"尖"); String xml = XStreamUtil.marshal(st, Student.class); System.out.println(StringUtils.formatXml(xml)); } }
View Code

一、@XmlRootElement:

  類級別的注解,將類映射為xml全局元素,也就是根元素。就像spring配置文件中的beans

  實例代碼:

@XmlRootElement public class Student { private String name; // 姓名
   private String sex; // 性別
   private int number; // 學號
   private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; } //省略下面代碼
View Code

  測試結果:

<?xml version="1.0" encoding="gb2312"?>
<student>
  <className>尖</className>
  <name>張三</name>
  <number>0</number>
  <sex>男</sex>
</student> Process finished with exit code 0
View Code

二、@XmlAccessorType :

  包和類級別的注解,javaEE的API對該注解的解釋是:控制字段是否被默認序列化。通俗來講,就是決定哪些字段或哪些get/set方法對應的字段會被映射為xml元素,需要注意的是字段或get/set方法的訪問權限(public/private)會影響字段是否被映射為xml元素,下面會詳細講解。
  注解只有一個value屬性,可取的值是一個名為XmlAccessType的枚舉類型里的值,下面詳細看一下這幾個值分別有什么用:

    2.1、XmlAccessType.PROPERTY:

      理解:下面是源碼里面的一段描述,這一段的大致翻譯是,《JAXB綁定類中的每個getter/setter對都將自動綁定到XML,除非由@link xmltinate注釋,只有當字段被一些JAXB注釋顯式注釋時,字段才綁定到XML》(注:這一段話需要格外注意的是,,該屬性可以自動將每個getter/setter綁定到xml,但不會自動綁定字段到XML)

/** * Every getter/setter pair in a JAXB-bound class will be automatically * bound to XML, unless annotated by {@link XmlTransient}. * * Fields are bound to XML only when they are explicitly annotated * by some of the JAXB annotations. */
View Code

      補充:

        (1)當使用了該值,只要字段有對應的get/set方法對(注意是成對出現,只有其中一個不會發生映射),不需要使用@XmlElement注解,不論該方法的訪問權限是什么(即使是private),jaxb就會將該字段映射成xml元素。不過最好加上@XmlElement注解,get/set方法任選一個即可,都加上會報錯

        (2)若在一個字段有set/get方法對但又在字段上添加@XmlElement注解會報屬性重復的錯誤

        (3)若沒有set/get方法對,則需要在字段上使用@XmlElement注解才可以映射為xml元素,否則不會發生映射

        (4)若get/set方法上使用了@XmlTransient注解,但想要對應字段發生映射,需要在對應字段上添加@XmlElement注解,此時不會報錯,並將該字段映射為xml元素。

    2.2、XmlAccessType.FIELD:

      理解:下面是源碼中的解釋,這一段的大致翻譯是,《jaxb綁定類中的每個非靜態、非瞬態字段都將自動綁定到XML,除非使用@XmlTransient進行注釋,只有當某些JAXB注釋顯式地對getter/setter對進行注釋時,它們才會綁定到XML》(注:這段需要注意的是,該屬性可以自動綁定類中的非靜態、非瞬態字段,但不會自動綁定getter/setter方法,正好與XmlAccessType.PROPERTY相反

 /** * Every non static, non transient field in a JAXB-bound class will be automatically * bound to XML, unless annotated by {@link XmlTransient}. * * Getter/setter pairs are bound to XML only when they are explicitly annotated * by some of the JAXB annotations. */
View Code

      補充:

        (1)每個非靜態的字段(無論訪問權限如何)都會被jaxb映射為xml元素,即使沒有get/set方法對,即使沒有使用@XmlElement元素,但最好加上該注解以表明該字段要被映射為xml元素

        (2)雖然沒有get/set方法對,也會發生映射,但加上get/set方法對也不會報錯,因為我們經常會使用這兩個方法。但注意,不能再在這兩個方法上使用@XmlElement方法,否則會報屬性重復的錯誤

        (3)若在字段上使用了@XmlTransient注解,但還想讓該字段發生映射,需要在該字段對應的get/set方法上添加@XmlElement

    2.3、XmlAccessType.PUBLIC_MEMBER (該值為默認值):

      注:如果不指定@XmlAccessorType的value值或者沒有使用此注解,在轉xml時會默認為value為XmlAccessType.PROPERTY,由下面的截圖可以清晰的看出默認值是XmlAccessType.PROPERTY     

@Inherited @Retention(RUNTIME) @Target({PACKAGE, TYPE}) public @interface XmlAccessorType { /** * Specifies whether fields or properties are serialized. * * @see XmlAccessType */ XmlAccessType value() default XmlAccessType.PUBLIC_MEMBER; }
View Code

      理解:下面則是對此的描述翻譯大概是《每個公共getter/setter對和每個公共字段都將自動綁定到XML,除非由@link xmltinate批注,私有、受保護或、默認為“僅包訪問”僅在以下情況下綁定到XML,由適當的JAXB注釋顯式注釋。》(注:在這里要格外注意XmlAccessType.PUBLIC_MEMBER屬性針對的是java對象中所有的public訪問權限的成員變量和通過getter/setter方式訪問的成員變量,而私有或者僅包訪問權限的字段,並不會自動綁定到XML          

/** * Every public getter/setter pair and every public field will be * automatically bound to XML, unless annotated by {@link XmlTransient}. * * Fields or getter/setter pairs that are private, protected, or * defaulted to package-only access are bound to XML only when they are * explicitly annotated by the appropriate JAXB annotations. */
View Code

      補充:

        (1)每個訪問權限為public的字段,或者每個訪問權限為public的get/set方法對,都會將字段映射為xml元素,即使不使用@XmlElement,但最好加上。不可同時存在public字段和對應的get/set方法對,不然會報屬性重復的錯誤

        (2)若使用@XmlElement注解,則實體類(注:實體類中的字段為私有private)中不能存在get/set方法或者只能在get/set上使用,否則會報屬性重復的錯誤

        (3)若字段不為public,get/set方法為public並使用了@XmlTransient,需要在字段上添加@XmlElement才會發生映射,若字段為public並使用了@XmlTransient,get/set方法對不為public,需要在get/set方法上使用@XmlElement才會映射。

    2.4、XmlAccessType.NONE:

      注: 這一段的翻譯是《任何字段或屬性都不會綁定到XML,除非使用某些JAXB注釋對它們進行特別注釋。

 /** * None of the fields or properties is bound to XML unless they * are specifically annotated with some of the JAXB annotations. */
View Code

      補充:

        任何字段,get/set方法對都不會發生映射,除非使用某些注解,如@XmlElement,@XmlElementWrapper等。

三、@XmlElement:

  字段,方法,參數級別的注解。該注解可以將被注解的字段(非靜態),或者被注解的get/set方法對應的字段映射為本地元素,也就是子元素。默認使用字段名或get/set方法去掉前綴剩下部分小寫作為元素名(在字段名和get/set方法符合命名規范的情況下)。

  屬性:該注解的屬性常用的屬性有如下

    (1)defaultValue:可以指定該元素默認的文本值

    (2)namespace:可以指定該元素所屬的命名空間

    (3)name: 同@XmlRootElement注解的name屬性一樣

    (4)required:可以指定該元素是否必須出現,默認為false

    (5)nillable: 可以指定元素的文本值是否可以為空,默認為false

@XmlRootElement(name = "Student") @XmlAccessorType(XmlAccessType.FIELD) public class Student { @XmlElement(name = "name",defaultValue = "hefeng") private String name; // 姓名
   @XmlElement(name = "sex", namespace = "Student") private String sex; // 性別
   @XmlElement(name = "number", required = true) private int number; // 學號
   @XmlElement(name = "className", nillable = true) private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; }//后面省略
View Code

  測試結果:

<?xml version="1.0" encoding="gb2312"?>
<Student xmlns:ns2="Student">
  <name>張三</name>
  <ns2:sex>男</ns2:sex>
  <number>0</number>
  <className>尖</className>
</Student>
View Code        

四、@XmlAttribute:

  字段和方法級別的注解。該注解會將字段或get/set方法對應的字段映射成本類對應元素的屬性,屬性名默認使用字段名或get/set方法去掉前綴剩下部分首字母小寫(在字段名和get/set方法符合命名規范的情況下)。修改上面例子:

  屬性:該注解有name,required,namespace三個屬性。用法和@XmlElement注解相同

@XmlRootElement(name = "Student") @XmlAccessorType() public class Student { private String name; // 姓名
   private String sex; // 性別
   private int number; // 學號
   private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; } @XmlAttribute(name="shiqingxue") public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; }
View Code

  測試結果:

<Student shiqingxue="張三">
  <className>尖</className>
  <number>0</number>
  <sex>男</sex>
</Student>
View Code

五、@XmlAccessorOrder:

  包和類級別的注解。控制生成元素的順序。

  屬性:該屬性有XmlAccessOrder.ALPHABETICAL 和 XmlAccessOrder.UNDEFINED兩種

    (1)XmlAccessOrder.ALPHABETICAL代表按照字母表的順序對生成的元素排序,也就是我們常說的字典順序

    (2)XmlAccessOrder.UNDEFINED,代表按照類中字段的順序生成元素的順序,也是該注解的默認值

  測試XmlAccessOrder.UNDEFINED

@XmlRootElement(name = "Student") @XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorOrder() public class Student { @XmlElement(name = "NAME") private String name; // 姓名
   @XmlElement(name = "SEX") private String sex; // 性別
   @XmlElement(name = "NUMBER") private int number; // 學號
   @XmlElement(name = "CLASS_NAME") private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; }//后面代碼省略……
View Code

  測試結果:可以看出字段的輸出順序是按照類中字段和屬性的順序

<?xml version="1.0" encoding="gb2312"?>
<Student>
  <NAME>張三</NAME>
  <SEX>男</SEX>
  <NUMBER>0</NUMBER>
  <CLASS_NAME>尖</CLASS_NAME>
</Student>
View Code

  測試XmlAccessOrder.ALPHABETICAL:

@XmlRootElement(name = "Student") @XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) public class Student { @XmlElement(name = "NAME") private String name; // 姓名
   @XmlElement(name = "SEX") private String sex; // 性別
   @XmlElement(name = "NUMBER") private int number; // 學號
   @XmlElement(name = "CLASS_NAME") private String className; // 班級

   public Student(){} public Student(String string, String string2, int i, String string3) { this.name = string; this.sex = string2; this.className = string3; }//后面代碼省略……
View Code

  測試結果:可以看出字段的輸出是按照字典順序排序的

<?xml version="1.0" encoding="gb2312"?>
<Student>
  <CLASS_NAME>尖</CLASS_NAME>
  <NAME>張三</NAME>
  <NUMBER>0</NUMBER>
  <SEX>男</SEX>
</Student>
View Code

六、@XmlElementWrapper

  字段和方法級別的注解:圍繞被映射的xml元素生成包裝元素。主要用在集合對象映射后生成包裝映射結果的xml元素,來看一下例子,創建兩個類Children、Father。

@XmlAccessorType(XmlAccessType.FIELD) public class Children{ @XmlElement(name = "USER_NAME") private String userName; public Children(){}; public Children(String userName){ this.userName = userName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
View Code
@XmlRootElement(name = "Father") @XmlAccessorType(XmlAccessType.FIELD) public class Father { @XmlElement(name = "AGE") private Integer age; @XmlElement(name = "Children") private List<Children> childrenList = new ArrayList<Children>(); public Father(){} public Father(Integer age){ this.age = age; childrenList.add(new Children("逝清雪")); childrenList.add(new Children("莫問")); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
View Code

  測試代碼:

public class Test { public static void main(String[] args) throws JAXBException { Father st = new Father(12); String xml = XStreamUtil.marshal(st, Father.class); System.out.println(StringUtils.formatXml(xml)); } }
View Code

  在沒有@XmlElementWrapper注解下的測試結果:

<?xml version="1.0" encoding="gb2312"?>
<Father>
  <AGE>12</AGE>
  <Children>
    <USER_NAME>逝清雪</USER_NAME>
  </Children>
  <Children>
    <USER_NAME>莫問</USER_NAME>
  </Children>
</Father>
View Code

  有@XmlElementWrapper注解下的測試結果:

<?xml version="1.0" encoding="gb2312"?>
<Father>
  <AGE>12</AGE>
  <List>
    <Children>
      <USER_NAME>逝清雪</USER_NAME>
    </Children>
    <Children>
      <USER_NAME>莫問</USER_NAME>
    </Children>
  </List>
</Father>
View Code

七、@XmlJavaTypeAdapter

  包、類、字段,方法、參數級別的注解:解決java日期(Date),數字(Number)格式化問題。直接看例子,修改Person類,添加一個Date類型字段:

  在這里要向使用@XmlJavaTypeAdapter我們就要指定一個指向將值類型轉換為綁定類型的類,這個類需要繼承XmlAdapter抽象類重寫里面的unmarshal和marshal方法

  DateAdapter工具類:

public class DateAdapter extends XmlAdapter<String, Date> { private SimpleDateFormat dateFormat =
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public String marshal(Date v) throws Exception { return dateFormat.format(v); } @Override public Date unmarshal(String v) throws Exception { return dateFormat.parse(v); } }
View Code

  測試代碼:

@XmlRootElement(name = "Children") @XmlAccessorType(XmlAccessType.FIELD) public class Children{ @XmlElement(name = "USER_NAME") private String userName; @XmlJavaTypeAdapter(DateAdapter.class) @XmlElement(name = "system_date") private Date systemDate; public Children(){}; public Children(String userName, Date data){ this.userName = userName; this.systemDate = data; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Date getSystemDate() { return systemDate; } public void setSystemDate(Date systemDate) { this.systemDate = systemDate; } }
View Code

  測試代碼:

public class Test { public static void main(String[] args) throws JAXBException { Children st = new Children("逝清雪", new Date()); String xml = XStreamUtil.marshal(st, Children.class); System.out.println(StringUtils.formatXml(xml)); } }
View Code

  測試結果:

<?xml version="1.0" encoding="gb2312"?>
<Children>
  <USER_NAME>逝清雪</USER_NAME>
  <system_date>2019-04-11 11:19:24</system_date>
</Children>
View Code     

 八、@XmlTransient:

  類,字段,方法級別的注解:可使JAXB在映射xml元素時忽略被注解的類,字段,get/set對應字段。需要注意的是該注解與所有其他JAXB注釋相互排斥,也就是說與其他注釋連用就會報錯

@XmlRootElement(name = "Children") @XmlAccessorType(XmlAccessType.FIELD) public class Children{ @XmlElement(name = "USER_NAME") private String userName; @XmlTransient private Date systemDate; public Children(){}; public Children(String userName, Date data){ this.userName = userName; this.systemDate = data; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Date getSystemDate() { return systemDate; } public void setSystemDate(Date systemDate) { this.systemDate = systemDate; } }
View Code

  測試代碼:

public class Test { public static void main(String[] args) throws JAXBException { Children st = new Children("逝清雪", new Date()); String xml = XStreamUtil.marshal(st, Children.class); System.out.println(StringUtils.formatXml(xml)); } }
View Code

  測試結果:

<?xml version="1.0" encoding="gb2312"?>
<Children>
  <USER_NAME>逝清雪</USER_NAME>
</Children>
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM