1. 程式人生 > >使用JAXB實現Bean與Xml相互轉換

使用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的列舉類。
  1. 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/