JAXB 简单使用

要点:

  • POJO 对象和 XML 互相转换。

  • POJO 类和 XSD 互相转换。

  • 主要介绍 JAXB 对简单 POJO 类(不含集合数组)的操作。

XML 文件头

XML 文件头的作用是说明 XML 标签的定义。常见的定义 XML 文档结构的文件有 DTD(Document Type Definition) 和 XSD(XML Schema Definition) 。

本文主要讲解对象是 XSD,所以 DTD 就不做介绍了。

1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

以上是 pom.xml 中的文件头部分,可以看到比较关键的配置信息有:xmlns,xmlns:xsi 和 xsi:schemaLocation。鉴于网络上 详解 XML 文件头的文章 已经很齐全了,这里就只简单列举一下我对文件头的理解。

  • xmlns:xsi

    用 URL 指定了一个唯一的名称,在标签中使用 xsi 前缀就可以使用这个唯一的名称。

  • xmlns

    默认的命名空间,指定了 xmlns 以后,可以省去在所有的 子元素 中使用前缀的工作。

  • xsi:schemaLocation

    schemaLocation 是 xsi 命名空间内的一个 属性 ,在这个特例下 xsi:schemaLocation 定义了 XML 命名空间和对应的 XSD 文档的位置的关系。

POJO 对象和 XML 互相转换

POJO 对象 和 XML 的相互转换用到了 Java SE 6 之后自带的 JAXB。

JAXB(Java Architecture for XML Binding简称JAXB)允许Java开发人员将Java类映射为XML表示方式。JAXB提供两种主要特性:将一个Java对象序列化为XML,以及反向操作,将XML解析成Java对象。换句话说,JAXB允许以XML格式存储和读取数据,而不需要程序的类结构实现特定的读取XML和保存XML的代码。

POJO 对象转换为 XML 文件

POJO 对象转换为 XML 文件需要借助注解来实现,注解的解释请见 JAXB 注解使用

首先定义一个 POJO 类:

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package cn.river.practice.msg_class;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"id",
"name",
"address",
"assestsAllocated"
})
@XmlRootElement(name = "employee")
public class Employee {

protected byte id;
@XmlElement(required = true)
protected String name;
@XmlElement(required = true)
protected Employee.Address address;
protected List<String> assestsAllocated;

public byte getId() {
return id;
}

public void setId(byte value) {
this.id = value;
}

public String getName() {
return name;
}

public void setName(String value) {
this.name = value;
}

public Employee.Address getAddress() {
return address;
}

public void setAddress(Employee.Address value) {
this.address = value;
}

public List<String> getAssestsAllocated() {
if (assestsAllocated == null) {
assestsAllocated = new ArrayList<String>();
}
return this.assestsAllocated;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"addressLine1",
"addressLine2",
"country",
"state",
"zip"
})
public static class Address {

@XmlElement(required = true)
protected String addressLine1;
@XmlElement(required = true)
protected String addressLine2;
@XmlElement(required = true)
protected String country;
@XmlElement(required = true)
protected String state;
protected short zip;

public String getAddressLine1() {
return addressLine1;
}

public void setAddressLine1(String value) {
this.addressLine1 = value;
}

public String getAddressLine2() {
return addressLine2;
}

public void setAddressLine2(String value) {
this.addressLine2 = value;
}

public String getCountry() {
return country;
}

public void setCountry(String value) {
this.country = value;
}

public String getState() {
return state;
}

public void setState(String value) {
this.state = value;
}

public short getZip() {
return zip;
}

public void setZip(short value) {
this.zip = value;
}
}
}

定义完 POJO 类以后,编写一个测试来将 POJO 对象转换为 XML 文件:

JAXBTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package cn.river.practice;

import org.junit.Test;
import cn.river.practice.msg_class.Employee;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.FileWriter;

public class JAXBTest {

/**
* 将java对象转换成xml,并用注解指定生成规则,是生成属性还是生成节点
* @throws Exception
*/
@Test
public void testMarshaller() throws Exception{
//获得转换的上下文对象
JAXBContext context = JAXBContext.newInstance(Employee.class);
//获得Marshaller对象
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

//填充对象
Employee employee = new Employee();
Employee.Address address = new Employee.Address();
employee.setId((byte)1);
employee.setName("test");
address.setAddressLine1("line1");
address.setAddressLine2("line2");
address.setCountry("cn");
address.setState("nono");
address.setZip((short)12122);
employee.setAddress(address);

//写入文件并且打印到控制台
FileWriter writer=new FileWriter(new File("employee.xml"));
marshaller.marshal(employee, writer);
marshaller.marshal(employee, System.out);
}
}

运行生成的 XML 文件如下:

employee.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<name>test</name>
<address>
<addressLine1>line1</addressLine1>
<addressLine2>line2</addressLine2>
<country>cn</country>
<state>nono</state>
<zip>12122</zip>
</address>
</employee>

XML 文件转换为 POJO 对象

编写一个测试来将刚才生成的 employee.xml 文件中的数据读取到相应的 POJO 类中:

JAXBTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JAXBTest {

/**
* 读取xml文档,并将xml文档反序列化为java对象
* @throws Exception
*/
@Test
public void testUnMarshaller() throws Exception{
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File f=new File("employee.xml");
Employee employee = (Employee) unmarshaller.unmarshal(f);
System.out.println("id: " + employee.getId());
System.out.println("name: " + employee.getName());
System.out.println("address_line1: " + employee.getAddress().getAddressLine1());
System.out.println("address_line2: " + employee.getAddress().getAddressLine2());
System.out.println("address_country: " + employee.getAddress().getCountry());
System.out.println("address_state: " + employee.getAddress().getState());
System.out.println("address_zip: " + employee.getAddress().getZip());
}
}

运行后的输出结果是:

1
2
3
4
5
6
7
id: 1
name: test
address_line1: line1
address_line2: line2
address_country: cn
address_state: nono
address_zip: 12122

POJO 类 和 XSD 文件互相转换

使用 JDK 自带的 xjc 和 schemagen 工具就可以在命令行中完成这项工作,这里我是在 IDEA 中用 JAXB2-maven-plugin 的方式来完成这个操作。

具体的 maven-plugin 操作可以见 官方文档

PS:官网给的 “Multiple schemas with different configuration” 例子有 bug,即插件无法读取到 configuration 中的信息(我也不知道是 plugin 本身的问题还是 maven 更新后 maven 自身的问题)。官方示例截图:

Multiple schemas with different configuration

JAXB2-maven-plugin_bug_config

这是一个还未解决的问题(在我写博客时),想查看 bug 是否修复,可以移步 github 中提出的关于这个问题的 issue


POJO 类转换为 XSD 文件

我们对之前的 Employee POJO 类进行转换,具体定义见上。

首先在 pom 文件中配置 maven-plugin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>schemagen</id>
<goals>
<goal>schemagen</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<!--这里的 source 可以指定一个目录,plugin 会自动递归地在目录中寻找 POJO 类来转换-->
<source>target/generated-sources/jaxb/cn/river/practice/msg_class/Employee.java</source>
</sources>
</configuration>
</plugin>
</plugins>
</build>

configuration 标签中的 sources 标签用来指定需要转换的 POJO 类,其他更多有关参数参见 schemagen 参数说明

配置完 pom 文件后,IDEA 会自动更新一下 maven-plugin 界面(如果不会请 reimport),然后在 IDEA 中点开在界面右边的 Maven Projects 窗口,再点开 Plugins,如图所示

IDEA maven-plugin 界面

这个时候如果设置好的 source 路径没有错误,直接双击 jaxb2:schemagen 就可以把指定的 POJO 类转换为 XSD 文件了。

转换效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">

<xs:element name="employee">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:byte"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="addressLine1" type="xs:string"/>
<xs:element name="addressLine2" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
<xs:element name="state" type="xs:string"/>
<xs:element name="zip" type="xs:short"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element maxOccurs="unbounded" minOccurs="0" name="assestsAllocated" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

XSD 文件转换为 POJO 类

接下来我们把生成的 XSD 文件转换为 POJO 类,同样我们先配置 pom 文件中的 plugin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<!--这里的 source 可以指定一个目录,plugin 会自动递归地在目录中寻找 XSD 文件来转换-->
<source>src/main/resources/xsd</source>
</sources>
<packageName>cn.river.practice.msg_class</packageName>
</configuration>
</plugin>

configuration 标签中的 sources 标签用来指定需要转换的 XSD 文件,packageName 标签用来指定生成的 POJO 类所在的包。其他更多有关参数参见 xjc 参数说明

之后的操作和 schemagen 差不多,在 IDEA 的 Maven Projects 中的 Plugins 更新后,直接双击 上图所示的 jaxb2:xjc 就可以生成 POJO 类了,生成的结果就是 Employee.java 文件。

IDEA XML 文件中报错 “uri is not registered”

解决这个问题最简单的方法是在相应的报错语句旁边点开 IDEA 小灯泡,然后选择 Fetch external resource 就可以了,如图:

IDEA 解决 XML URI 问题

网络上有很多解决方案都是直接 Ignore external resource ,总觉得有点不大妥当。