JAXB 深入显出 - JAXB 教程 XML转Java对象深入(Unmarshaller)

x33g5p2x  于2021-12-28 转载在 其他  
字(7.8k)|赞(0)|评价(0)|浏览(276)

摘要: 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的支持场景。

相关文章