JAXB 深入顯出 - JAXB 教程 XML轉Java物件深入(Unmarshaller)
摘要: JAXB 作為JDK的一部分,能便捷地將Java物件與XML進行相互轉換,本教程從實際案例出發來講解JAXB 2 的那些事兒。完整版目錄
前情回顧
上一節以簡單介紹了 UnMarshaller 的過程,主要介紹了多種資料來源如何處理。這一節將深入介紹XML資料轉換為JAVA物件,將涉及更加複雜的XML結構。
Unmarshaller Callback
在JAXB反序列化時,有兩個方法可以自定義一些行為,它們有固定的形式:
void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {}
void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {}
上一節中的Employe經過改造:
package com.example.demo.lesson17;
import java.io.Serializable;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name= "Employe" )
@XmlAccessorType(XmlAccessType.FIELD)
public class Employe implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
System.out.println("呼叫Unmarshaller之前");
}
void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
System.out.println("呼叫Unmarshaller之後");
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
}
測試一下:
@Test
public void test1() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Employe.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xmlStr = "<Employe><id>1504</id><name>Test</name></Employe>";
Employe employe = (Employe)unmarshaller.unmarshal(new StringReader(xmlStr));
System.out.println(employe);//Employee [id=1504, name=Test]
}
結果:
呼叫Unmarshaller之前
呼叫Unmarshaller之後
Employee [id=1504, name=Test]
這兩個方法在某些場合可以起到自定義 Unmarshaller 的效果。
複雜XML轉化
之前見到的XML都是一層結構,真實場景中的XML肯定複雜的多。但是對於特別複雜的XML結構,我不會深入,因為XML都是一級一級的結構,再複雜的XML也是多級拼接而成,於是正確的拆分XML便是反序列化中重要的一環。
<employee id="17">
<department>
<id>101</id>
<name>IT</name>
</department>
<firstName>Lokesh</firstName>
<lastName>Gupta</lastName>
</employee>
對於這個XML,需要把XML拆分成兩部分:最外層的employe和巢狀的department。如果遇到XML中是屬性的,在Java bean的欄位上添加註解@XmlAttribute
,需要名稱不一樣的,新增別名@XmlElement(name="FirstName")
。
Employee的部分重要程式碼:
@XmlRootElement(name = "employee")
@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
@XmlAttribute
private Integer id;
@XmlElement(name="FirstName")
private String firstName;
private String lastName;
private Department department;
// ignore setters/getters,toString
}
Department的完整程式碼:
package com.example.demo.lesson17;
public class Department {
public String id;
public String name;
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + "]";
}
}
因為比較簡單,不需要任何註解,看起來好像和JAXB無關的Department。
測試一下:
@Test
public void test2() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xmlStr = "<employee id='17'>\r\n" +
" <department>\r\n" +
" <id>101</id>\r\n" +
" <name>IT</name>\r\n" +
" </department>\r\n" +
" <FirstName>Lokesh</FirstName>\r\n" +
" <id>1</id>\r\n" +
" <lastName>Gupta</lastName>\r\n" +
"</employee>";
Employee employee = (Employee)unmarshaller.unmarshal(new StringReader(xmlStr));
System.out.println(employee);//Employee [id=17, firstName=Lokesh, lastName=Gupta, department=Department [id=101, name=IT]]
}
得到的結果:
Employee [id=17, firstName=Lokesh, lastName=Gupta, department=Department [id=101, name=IT]]
所有的欄位都反序列化成功了,不僅僅得到了Employee,還得到了Department資料。
有的XML顯得更為複雜,像下面這種:
<MUSEUMS>
<MUSEUM children_allowed="false">
<MUSEUM_NAME>Reina Sofia Museum</MUSEUM_NAME>
<CITY>Madrid</CITY>
<PERMANENT_EXHIBITION>
<NAME>Permanent Exhibition - Reina Sofia Museum</NAME>
<ARTIST>Picasso</ARTIST>
<ARTIST>Dali</ARTIST>
<ARTIST>Miro</ARTIST>
<FROM>1900-01-01</FROM>
<TO>2014-12-31</TO>
</PERMANENT_EXHIBITION>
</MUSEUM>
<MUSEUM>
<MUSEUM_NAME>Louvre Museum</MUSEUM_NAME>
<CITY>Paris</CITY>
<PERMANENT_EXHIBITION>
<NAME>Permanent Exhibition - Louvre Museum</NAME>
<ARTIST>Leonardo da Vinci</ARTIST>
<ARTIST>Caravaggio</ARTIST>
<ARTIST>Delacroix</ARTIST>
</PERMANENT_EXHIBITION>
</MUSEUM>
<TOTAL>2</TOTAL>
</MUSEUMS>
這種XML結構在業務場景更普遍,它們很長,看著很複雜,其實並沒有想象的那麼難。這個資料看著複雜,其實更多的是重複,既然都是一樣的,那就需要巧用‘迴圈’了。而Java bean中的迴圈當然要考慮List,它裡面包含的資料都是相同型別,結構必然一樣,把上面這段XML拆分後,就得到了3個Java bean。
最外層的Java bean:
@XmlRootElement(name = "MUSEUMS")
@XmlAccessorType(XmlAccessType.FIELD)
public class Museums {
@XmlElement(name = "MUSEUM")
List<Museum> museums;
@XmlElement(name = "TOTAL")
String total;
// ignore setters/getters,toString
}
接下來的Meseum:
@XmlRootElement( name = "MUSEUM" )
@XmlType( propOrder = { "name", "city", "special" } )
@XmlAccessorType(XmlAccessType.FIELD)
public class Museum {
@XmlElement(name = "MUSEUM_NAME")
String name;
@XmlAttribute(name = "children_allowed", required=false)
Boolean childrenAllowed;
@XmlElement(name = "CITY")
String city;
@XmlElement(name = "PERMANENT_EXHIBITION")
Exhibition special;
// ignore setters/getters,toString
}
接下來的一層:
@XmlAccessorType(XmlAccessType.FIELD)
public class Exhibition {
@XmlElement(name = "NAME")
String name;
@XmlElement(name = "ARTIST")
List<String> artist;
@XmlElement(name = "FROM")
String from;
@XmlElement(name = "TO")
String to;
// ignore setters/getters,toString
}
演示程式碼比較長:
@Test
public void test3() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Museums.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xmlStr = "<MUSEUMS>\r\n" +
" <MUSEUM children_allowed=\"false\">\r\n" +
" <MUSEUM_NAME>Reina Sofia Museum</MUSEUM_NAME>\r\n" +
" <CITY>Madrid</CITY>\r\n" +
" <PERMANENT_EXHIBITION>\r\n" +
" <NAME>Permanent Exhibition - Reina Sofia Museum</NAME>\r\n" +
" <ARTIST>Picasso</ARTIST>\r\n" +
" <ARTIST>Dali</ARTIST>\r\n" +
" <ARTIST>Miro</ARTIST>\r\n" +
" <FROM>1900-01-01</FROM>\r\n" +
" <TO>2014-12-31</TO>\r\n" +
" </PERMANENT_EXHIBITION>\r\n" +
" </MUSEUM>\r\n" +
" <MUSEUM>\r\n" +
" <MUSEUM_NAME>Louvre Museum</MUSEUM_NAME>\r\n" +
" <CITY>Paris</CITY>\r\n" +
" <PERMANENT_EXHIBITION>\r\n" +
" <NAME>Permanent Exhibition - Louvre Museum</NAME>\r\n" +
" <ARTIST>Leonardo da Vinci</ARTIST>\r\n" +
" <ARTIST>Caravaggio</ARTIST>\r\n" +
" <ARTIST>Delacroix</ARTIST>\r\n" +
" </PERMANENT_EXHIBITION>\r\n" +
" </MUSEUM>\r\n" +
" <TOTAL>2</TOTAL>\r\n" +
"</MUSEUMS>";
Museums museums = (Museums)unmarshaller.unmarshal(new StringReader(xmlStr));
System.out.println(museums);
//Museums [museums=[Museum [name=Reina Sofia Museum, childrenAllowed=false, city=Madrid, special=Exhibition [name=Permanent Exhibition - Reina Sofia Museum, artist=[Picasso, Dali, Miro], from=1900-01-01, to=2014-12-31]], Museum [name=Louvre Museum, childrenAllowed=null, city=Paris, special=Exhibition [name=Permanent Exhibition - Louvre Museum, artist=[Leonardo da Vinci, Caravaggio, Delacroix], from=null, to=null]]], total=2]
}
XML中所有的元素都被正確識別。
無論多麼複雜的XML結構,都離不開這幾種拼接形式,巢狀的越多,需要越多的Java物件與之對應。
對於一些需要特殊處理的資料,需要使用到介面卡來私人訂製。
完整程式碼
可以在GitHub找到完整程式碼。
本節程式碼均在該包下:package com.example.demo.lesson17;
下節預覽
本節介紹了 JAXB 將複雜 XML 轉化為Java物件。下一節開始,講述JAXB對於JSON的支援場景。