1. 程式人生 > >JAXB 深入顯出 - JAXB 教程 XML轉Java物件深入(Unmarshaller)

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的支援場景。