使用JAXB實現Bean與Xml相互轉換
最近幾天,我自己負責的應用這邊引入了一個新的合作方,主要是我這邊呼叫他們的介面,但是有個很坑的地方,他們傳參居然不支援json格式,並且只支援xml格式進行互動,於是自己寫了一個工具類去支援bean與xml互相轉換,json與xml之間互相轉換。
JAXBContext 介面
我在這裡使用了rt.jar 下javax下的 JAXBContext 介面,它提供了 JAXB API 的客戶端入口點。它提供了管理實現 JAXB 繫結框架操作所需的 XML/Java 繫結資訊的抽象。
- 獲取 JAXBContext 介面例項
JAXBContext 介面為我們提供了獲取 JAXBContext 例項的 newInstance 方法。將需要轉換為xml的bean的class傳入即可。
JAXBContext context = JAXBContext.newInstance(zlass);
Marshaller 介面
- 獲取 Marshaller 介面例項
JAXBContext 例項為我們提供了獲取 Marshaller 例項的 createMarshaller方法。
Marshaller marshaller = context.createMarshaller();
Marshaller 介面主要是將java物件序列化成xml字串,是通過 marshal 方法。
/** * JAVA bean 轉 xml * @param obj * @param zlass * @return * @throws JAXBException*/ public static String beanToXml (Object obj, Class<?> zlass) throws JAXBException { JAXBContext context = JAXBContext.newInstance(zlass); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING,"GBK"); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false); StringWriter writer = new StringWriter(); marshaller.marshal(obj,writer); return writer.toString(); }
Marshaller 介面中還定義了5個屬性,分別是:
- JAXB_ENCODING
這個屬性是設定編碼集,
marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
- JAXB_FORMATTED_OUTPUT
這個屬性是是否格式化生成的xml串 true-格式化,false-不格式化
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
- JAXB_SCHEMA_LOCATION
指定xsi:schemaLocation,它定義了XML Namespace和對應的XSD(Xml Schema Definition)文件的位置的關係。它的值由一個或多個URI引用對組成,兩個URI之間以空白符分隔(空格和換行均可)。第一個URI是定義的XML Namespace的值,第二個URI給出Schema文件的位置,Schema處理器將從這個位置讀取Schema文件,該文件的targetNamespace必須與第一個URI相匹配。
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "xxx.xxx.xxx");
- JAXB_NO_NAMESPACE_SCHEMA_LOCATION
如果沒有Namespeace,但是需要使用Schema,就需要用到JAXB_NO_NAMESPACE_SCHEMA_LOCATION,它可以指定將放置在已編組 XML 輸出中的 xsi:noNamespaceSchemaLocation 屬性值
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "xxx.xxx.xxx");
- JAXB_FRAGMENT
是否省略xml頭資訊()true-省略,false-不省略
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
Unmarshaller 介面
- 獲取 Unmarshaller 介面例項
JAXBContext 例項為我們提供了獲取 Unmarshaller 例項的 createUnmarshaller 方法,需要注意的是,反序列化時需要將轉換的bean上加上無參構造器。
Unmarshaller unmarshaller = context.createUnmarshaller();
Marshaller 介面主要是將java物件序列化成xml字串,是通過 unmarshal 方法。
/** * xml 轉 JAVA bean * @param xml * @param zlass * @return * @throws JAXBException */ public static Object xmlToBean (String xml, Class<?> zlass) throws JAXBException { JAXBContext context = JAXBContext.newInstance(zlass); Unmarshaller unmarshaller = context.createUnmarshaller(); Object object = unmarshaller.unmarshal(new StringReader(xml)); return object; }
JAXB相關的重要Annotation
我在這定義3個類,來簡單說一下下面幾個常用的註解的作用,與用法。
首先是一個班級類
@XmlRootElement(name = "class") public class ClassAndGrade { private ClassAndGradeMsg classAndGrade; private List<Student> students; @XmlElement(name="classAndGrade") public ClassAndGradeMsg getClassAndGrade() { return classAndGrade; } public void setClassAndGrade(ClassAndGradeMsg classAndGrade) { this.classAndGrade = classAndGrade; } @XmlElementWrapper(name="students") @XmlElement(name="student") public List<Student> getStudents() { return students; } public void setStudents(List<Student> students) { this.students = students; } }
然後再定義一個班級資訊類
public class ClassAndGradeMsg { private Integer personNum; private String name; private String classTeacher; /** * @Title:ClassAndGradeMsg * @param:@param personNum * @param:@param name * @param:@param classTeacher * @throws */ public ClassAndGradeMsg(Integer personNum, String name, String classTeacher) { super(); this.personNum = personNum; this.name = name; this.classTeacher = classTeacher; } @XmlElement(name="personNum") public Integer getPersonNum() { return personNum; } public void setPersonNum(Integer personNum) { this.personNum = personNum; } @XmlElement(name="name") public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name="classTeacher") public String getClassTeacher() { return classTeacher; } public void setClassTeacher(String classTeacher) { this.classTeacher = classTeacher; } }
再定義以後學生類
public class Student { private String name; private Integer age; private String sex; /** * @Title:Student * @param:@param name * @param:@param age * @param:@param sex * @throws */ public Student(String name, Integer age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } @XmlElement(name="name") public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name="age") public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @XmlElement(name="sex") public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
最後再寫一個測試main方法
public static void main(String[] args) throws ClassNotFoundException { ClassAndGrade classAndGrade = new ClassAndGrade(); ClassAndGradeMsg classAndGradeMsg = new ClassAndGradeMsg(2, "三年二班", "張三"); classAndGrade.setClassAndGrade(classAndGradeMsg); List<Student> list = new ArrayList<Student>(); Student s1 = new Student("李四", 12, "男"); Student s2 = new Student("王五", 12, "女"); list.add(s1); list.add(s2); classAndGrade.setStudents(list); try { String xml = XmlUtil.beanToXml(classAndGrade, ClassAndGrade.class); System.out.println(xml); } catch (Exception e) { e.printStackTrace(); } }
- @XmlRootElement
類級別的註解,這個註解為根節點的註解,加在類上面,而且為必要的註解,如果沒有此註解,執行beanToXml方法時將會報異常。這個根節點預設名字為類名,但是可以設定name屬性來修改根節點名字。namespace屬性可以用於指定生成的元素所屬的名稱空間。
@XmlRootElement(name = "class") public class ClassAndGrade {
執行結果如下,ClassAndGrade 被修改為class:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <class> <classAndGrade> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGrade> <students> <student> <age>12</age> <name>李四</name> <sex>男</sex> </student> <student> <age>12</age> <name>王五</name> <sex>女</sex> </student> </students> </class>
- @XmlElement
欄位,方法級別的註解,將java類的屬性對映為xml的一個結點。一般使用在屬性上,或者get方法上,其中常用的屬性有name、nillable、namespace、defaultValue。name可以設定結點的名稱;nillable 指定文字是否可以為空,true-可以為空,false-不可以為空,預設為false,如果設定為true,則該欄位為空是,這個結點也會生成,但是值為空,如果是指為false,則該結點不生成;namespace屬性可以用於指定生成的元素所屬的名稱空間;defaultValue 可以設定該結點的預設文字。
@XmlElement(name="sex", nillable = false, defaultValue = "女") public String getSex() { return sex; }
- @XmlTransient
類,欄位,方法級別的註解。當新增這個註解後,這個屬性或者類將不進行對映。需要注意的是該註解與所有其他JAXB註解相互排斥.
@XmlTransient public Integer getAge() { return age; }
現在執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <name>李四</name> <sex>男</sex> </student> <student> <name>王五</name> <sex>女</sex> </student> </students> </classAndGrade>
age結點被剔除
- @XmlAccessorType
類級別註解,其中有一個value屬性,值為XmlAccessType的列舉類。
- XmlAccessType.PROPERTY 加這個value表示,會將所有擁有get方法和set方法的屬性(必須2個方法都有,否則不對映)對映成xml,除非加入@XmlTransient則不會對映,如果沒有get/set方法,則需要再屬性上加上@XmlElement。
@XmlAccessorType(value = XmlAccessType.PROPERTY) public class Student { private String name; private Integer age; @XmlElement(name="sex", nillable = false, defaultValue = "女") private String sex; /** * @Title:Student * @param:@param name * @param:@param age * @param:@param sex * @throws */ public Student(String name, Integer age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } }
我在Student類中加上@XmlAccessorType註解,並且在sex屬性上加上@XmlElement註解,給name屬性加上get/set方法,給age只加了get方法,執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <name>李四</name> <sex>男</sex> </student> <student> <name>王五</name> <sex>女</sex> </student> </students> </classAndGrade>
上例可以看出只顯示了name和sex結點,而沒有顯示age結點。
2.XmlAccessType.FIELD
這個屬性是將類中非靜態的屬性都對映到xml中,並且不需要加get/set方法
@XmlAccessorType(value = XmlAccessType.FIELD) public class Student { private String name; private Integer age; private static String sex; /** * @Title:Student * @param:@param name * @param:@param age * @param:@param sex * @throws */ public Student(String name, Integer age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } }
我在將sex屬性寫為靜態,執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <age>12</age> <name>李四</name> </student> <student> <age>12</age> <name>王五</name> </student> </students> </classAndGrade>
sex 確實沒有被對映。
3.XmlAccessType.PUBLIC_MEMBER
這個屬性值,是@XmlAccessorType的預設預設值,它會將屬性為public的屬性或者get/set方法同時為public的屬性對映成xml。
@XmlAccessorType(value = XmlAccessType.PUBLIC_MEMBER) public class Student { public String name; private Integer age; public String sex; /** * @Title:Student * @param:@param name * @param:@param age * @param:@param sex * @throws */ public Student(String name, Integer age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } private String getName() { return name; } private void setName(String name) { this.name = name; } public Integer getAge() { return age; } }
上例中 name 的屬性為public的 get/set方法為private的,age的屬性為private的且只有一個get方法,sex只有一個public的方法執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <name>李四</name> <sex>男</sex> </student> <student> <name>王五</name> <sex>女</sex> </student> </students> </classAndGrade>
4.XmlAccessType.NONE
這個屬性表示任何屬性都不會被對映到xml中,除非使用其他註解,如@XmlElement
@XmlAccessorType(value = XmlAccessType.NONE) public class Student { @XmlElement(name="name") public String name; private Integer age; public String sex; /** * @Title:Student * @param:@param name * @param:@param age * @param:@param sex * @throws */ public Student(String name, Integer age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } }
執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <name>李四</name> </student> <student> <name>王五</name> </student> </students> </classAndGrade>
只映射了name
- @XmlAccessorOrder
類級別的註解。控制生成屬性對映xml結點的順序。其中有一個value屬性,可以設定排序方式,XmlAccessOrder.ALPHABETICAL 為按照字母順序進行排序, XmlAccessOrder.UNDEFINED按照屬性順序進行排序,預設為XmlAccessOrder.UNDEFINED
@XmlAccessorOrder(value = XmlAccessOrder.ALPHABETICAL) public class Student {
現在執行結果如下:
<?xml version="1.0" encoding="GBK" standalone="yes"?> <classAndGrade> <classAndGradeMsg> <classTeacher>張三</classTeacher> <name>三年二班</name> <personNum>2</personNum> </classAndGradeMsg> <students> <student> <age>12</age> <name>李四</name> <sex>男</sex> </student> <student> <age>12</age> <name>王五</name> <sex>女</sex> </student> </students> </classAndGrade>
- @XmlJavaTypeAdapter
這個註解主要是解決一些資料格式化問題的,比如時間格式化。
我在我的Student類中加入time欄位。
public Date time;
但是直接執行的話這個xml對映的結果為:
<time>2019-01-04T17:52:38.158+08:00</time>
但是,這不是我想要的結果,我需要格式化之後的資料。
建立時間格式化類Dateformatting
public class Dateformatting extends XmlAdapter<String, Date> { private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); @Override public Date unmarshal(String date) throws Exception { return SDF.parse(date); } @Override public String marshal(Date date) throws Exception { return SDF.format(date); } }
在時間屬性上加上@XmlJavaTypeAdapter(Dateformatting.class)
@XmlJavaTypeAdapter(Dateformatting.class) public Date time;
執行結果:
<time>2019-01-04 05:59:42</time>
此時時間已經被格式化成我們想要的格式。
- @XmlElementWrapper
這個註解是載入集合上面的,我在上面學生List上就是加了這個註解。
@XmlElementWrapper(name="students") @XmlElement(name="student") public List<Student> getStudents() { return students; }
- @XmlAttribute
這個註解會將屬性變為上一個結點的屬性
@XmlAttribute(name="name") public String name;
執行結果為
<student name="李四"> <sex>男</sex> <time>2019-01-04 06:06:38</time> </student>
- @XmlType
類級別的註解,這個註解可以自定義排序,使用propOrder 屬性。
@XmlType(propOrder = {"time", "sex", "age", "name"}) public class Student {
執行結果為:
<student> <time>2019-01-04 06:13:58</time> <sex>女</sex> <age>12</age> <name>王五</name> </student>
上面就是我們在使用JAXB時常用的一些註解,以及用法。
最後附上我自己使用的xmlUtil的工具類。
/** * xml util * @author wuyouxin * */ public class XmlUtil { /** * JAVA bean 轉 xml * @param obj * @param zlass * @return * @throws JAXBException */ public static String beanToXml (Object obj, Class<?> zlass) throws JAXBException { JAXBContext context = JAXBContext.newInstance(zlass); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK"); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false); StringWriter writer = new StringWriter(); marshaller.marshal(obj,writer); return writer.toString(); } /** * xml 轉 JAVA bean * @param xml * @param zlass * @return * @throws JAXBException */ public static Object xmlToBean (String xml, Class<?> zlass) throws JAXBException { JAXBContext context = JAXBContext.newInstance(zlass); Unmarshaller unmarshaller = context.createUnmarshaller(); Object object = unmarshaller.unmarshal(new StringReader(xml)); return object; } }
-------------------- END ---------------------
最後附上作者的微信公眾號地址和部落格地址
公眾號:wuyouxin_gzh
Herrt灬凌夜:https://www.cnblogs.com/wuyx/