XML是开发中常用的配置文件类型,在常用的组件包里也能时常看到它的踪影,如Spring中的applicationContext.xml、Hibernate中的hbm.xml,都是用的XML来配置相关的参数信息。可见其使用是十分广泛的。
但是在Java中,要解析一个XML,并封装为一个对象,可能大家首先会想到Dom4j、Jdom等第三方类库,使用其中的解析器对每一个节点解析,并生成自己想要的对象。这样写的话,代码显得是很混乱,不是很易读。如果在Webservice交互中,用到XML传递数据,那么工作量就会更大了。
今天就要用一个非常好用的工具来解决这个问题。它就是JAXB类库。
一、介绍
JAXB允许JAVA开发人员将JAVA类映射为XML,也可以逆向的将XML转换为JAVA。可以不用手动的去写解析XML的方法,只需要书写对应的JAVA Bean,并配置好相应的注解,就可以完成转换,大大简化了程序员的工作量。
二、集成
讲了这么多,大家可能在想,如何集成呢,其实咱们不用集成就可以使用,这个功能是直接集成在JDK中的。完全不用集成,是不更简单呢,不用集成任何类库。
三、前置学习
JAXB实现转换是依赖于Annotation的,那么咱们应该如何使用呢,可以看下面各个Annotation的作用:
a)、@XmlRootElement
用来定义一个XML的根节点信息,其参数有name和namespace。
其中name用来定义根节点的名称,非必填。
namespace用来定义XML的命名空间,非必填。
例如下定义:
如果咱们定义了一个JAVA Bean,名叫Property:
1
|
public
class
Property
{
}
|
1.咱们给他加上@XmlRootElement信息:
1
2
|
@XmlRootElement
public
class
Property
{
}
|
那么用表明生成的XML的根节点就是类名的小写,如下:
1
2
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<property/>
|
2.如果定义了name信息,则根节点名称就会是你定义的名称:
1
2
3
|
@XmlRootElement
(
name
=
"Test"
)
public
class
Property
{
}
|
生成的XML如下:
1
2
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<Test/>
|
3.如果定义了namespace,根节点就会显示出你定义的namespace,如下代码
1
2
|
@XmlRootElement
(
name
=
"Test"
,
namespace
=
"com.jialeens"
)
public
class
Property
{
}
|
生成的XML如下
1
2
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<Test
xmlns
=
"com.jialeens"
/>
|
b)、@XmlAccessorType
控制字段或属性的序列化。常用的有XmlAccessType.FIELD、XmlAccessType.PROPERTY和XmlAccessType.NONE。
如果定义的是FIELD(全自动),则表明JAXB 绑定类中的每个非静态(static)、非瞬态(transient)字段将会自动绑定到 XML,除非由 XmlTransient 注释。
如果定义的是PROPERTY(半自动),则表明JAXB 绑定类中的每个获取方法(get)/设置方法(set)对将会自动绑定到 XML,除非由 XmlTransient 注释。需要注意的是要实现get/set方法。
如果定义的是NONE(手动),则表所有字段或属性都不能绑定到 XML,除非使用一些 JAXB 注释专门对它们进行注释。如果使用了这个属性,咱们就需要对每个要映射的成员变量做设置才可以。
1.XmlAccessType.FIELD
1
2
3
4
5
|
@XmlRootElement
(
name
=
"Test"
)
@XmlAccessorType
(
XmlAccessType
.
FIELD
)
public
class
Property
{
public
String
name
;
}
|
生成的xml如下
1
2
3
4
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<Test>
<name>
a
<
/
name
>
<
/
Test
>
|
2.XmlAccessType.PROPERTY
如果咱们定义的时候不生成get/set方法,看看效果。
1
2
3
4
5
|
@XmlRootElement
(
name
=
"Test"
)
@XmlAccessorType
(
XmlAccessType
.
PROPERTY
)
public
class
Property
{
public
String
name
;
}
|
输出结果
1
2
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<
Test
/
>
|
可以看到没有生成name的内容。
咱们现在生成get/set方法,再看看效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@XmlRootElement
(
name
=
"Test"
)
@XmlAccessorType
(
XmlAccessType
.
PROPERTY
)
public
class
Property
{
public
String
name
;
public
String
getName
(
)
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
}
|
生成XML的结果如下
1
2
3
4
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<Test>
<name>
a
<
/
name
>
<
/
Test
>
|
可以看到,这个类型是和get/set方法是否存在相关,如果写get/set方法,就生成xml。
c)、@XmlElement
将Java类的一个属性映射到与属性同名的一个XML元素。例如上面的例子,生成的是一个子标签。
1
2
3
4
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<Test>
<name>
a
<
/
name
>
<
/
Test
>
|
d)、@XmlAttribute
将Java类的一个属性映射到与属性同名的一个XML属性。
1
2
3
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<
Test
name
=
"a"
>
<
/
Test
>
|
如上,name是在Test标签内部,当作Test标签的一个属性。
e)、@XmlElementWrapper
对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。
f)、@XmlTransient
阻止将 JavaBean 属性映射到 XML 表示形式。主要用来解决映射冲突的情况。
四、简单使用
上面说了这么多,大家可能还不是很理解到底应该怎么用,下面咱们来讲一个简单的例子。
XML-->JAVA
现在咱们有一段XML。如下结构。
1
2
3
4
5
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<
property
type
=
"string"
>
<key>
testKey
<
/
key
>
<value>
testValue
<
/
value
>
<
/
property
>
|
咱们先分析一下这个XML,可以看到这个XML的根节点是property。其中有个属性是type。其下面有两个标签,分别是key和value。那么咱们可以先定义一个bean。
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
|
public
class
Property
{
/**
* 键
*/
private
String
key
;
/**
* 值
*/
private
String
value
;
/**
* 类型
*/
private
String
type
;
/**
* @return the key
*/
public
String
getKey
(
)
{
return
key
;
}
/**
* @param key
* the key to set
*/
public
void
setKey
(
String
key
)
{
this
.
key
=
key
;
}
/**
* @return the value
*/
public
String
getValue
(
)
{
return
value
;
}
/**
* @param value
* the value to set
*/
public
void
setValue
(
String
value
)
{
this
.
value
=
value
;
}
/**
* @return the type
*/
public
String
getType
(
)
{
return
type
;
}
/**
* @param type
* the type to set
*/
public
void
setType
(
String
type
)
{
this
.
type
=
type
;
}
}
|
现在咱们先定义一下根节点,根节点咱们使用@XmlRootElement注解。这样配置@XmlRootElement(name="property")。
上面的XML里有一个属性type,咱们可以使用@XmlAttribute(name = "type")来标注type的成员变量
对于name和value这两个成员变量,可以使用@XmlElement来标注。那么咱们的JAVA Bean可以改成这样。
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
|
import
javax
.
xml
.
bind
.
annotation
.
XmlAccessType
;
import
javax
.
xml
.
bind
.
annotation
.
XmlAccessorType
;
import
javax
.
xml
.
bind
.
annotation
.
XmlAttribute
;
import
javax
.
xml
.
bind
.
annotation
.
XmlElement
;
import
javax
.
xml
.
bind
.
annotation
.
XmlRootElement
;
@XmlAccessorType
(
XmlAccessType
.
FIELD
)
@XmlRootElement
public
class
Property
{
/**
* 键
*/
@XmlElement
(
name
=
"key"
)
private
String
key
;
/**
* 值
*/
@XmlElement
(
name
=
"value"
)
private
String
value
;
/**
* 类型
*/
@XmlAttribute
(
name
=
"type"
)
private
String
type
;
/**
* @return the key
*/
public
String
getKey
(
)
{
return
key
;
}
/**
* @param key
* the key to set
*/
public
void
setKey
(
String
key
)
{
this
.
key
=
key
;
}
/**
* @return the value
*/
public
String
getValue
(
)
{
return
value
;
}
/**
* @param value
* the value to set
*/
public
void
setValue
(
String
value
)
{
this
.
value
=
value
;
}
/**
* @return the type
*/
public
String
getType
(
)
{
return
type
;
}
/**
* @param type
* the type to set
*/
public
void
setType
(
String
type
)
{
this
.
type
=
type
;
}
}
|
咱们现在写一个调用的测试类。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
class
JaxbTest
{
@Test
public
void
test
(
)
throws
JAXBException
,
IOException
{
String
xml
=
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+
"<property type=\"string\">"
+
"<key>testKey</key>"
+
"<value>testValue</value>"
+
"</property>"
;
JAXBContext
context
;
context
=
JAXBContext
.
newInstance
(
Property
.
class
)
;
Unmarshaller
unmarshal
=
context
.
createUnmarshaller
(
)
;
Property
obj
=
(
Property
)
unmarshal
.
unmarshal
(
new
StringReader
(
xml
)
)
;
System
.
out
.
println
(
obj
.
getKey
(
)
)
;
System
.
out
.
println
(
obj
.
getValue
(
)
)
;
System
.
out
.
println
(
obj
.
getType
(
)
)
;
}
}
|
是不是很简单,完全没有解析XML的代码。
输出结果
1
2
3
|
testKey
testValue
string
|
JAVA-->XML
咱们还是用上面的例子来将一个JAVA Bean转换成一个XML,咱们只需要修改调用方法即可,如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
class
JaxbTest
{
@Test
public
void
test
(
)
throws
JAXBException
,
IOException
{
Property
a
=
new
Property
(
)
;
a
.
setKey
(
"testKey"
)
;
a
.
setValue
(
"testValue"
)
;
a
.
setType
(
"string"
)
;
ByteArrayOutputStream
os
=
new
ByteArrayOutputStream
(
)
;
try
{
JAXBContext
jc
=
JAXBContext
.
newInstance
(
a
.
getClass
(
)
)
;
Marshaller
m
=
jc
.
createMarshaller
(
)
;
m
.
setProperty
(
Marshaller
.
JAXB_FORMATTED_OUTPUT
,
true
)
;
m
.
marshal
(
a
,
os
)
;
String
xml
=
new
String
(
os
.
toByteArray
(
)
,
"UTF-8"
)
;
System
.
out
.
println
(
xml
)
;
}
finally
{
os
.
close
(
)
;
}
}
}
|
结果如下:
1
2
3
4
5
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<property
type
=
"string"
>
<key>
testKey
</key>
<value>
testValue
</value>
</property>
|
五、实现一个通用的工具类
咱们可以针对上面两种转换的方式写一个工具类,如下:
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
|
import
java
.
io
.
ByteArrayOutputStream
;
import
java
.
io
.
IOException
;
import
java
.
io
.
InputStream
;
import
java
.
io
.
StringReader
;
import
javax
.
xml
.
bind
.
JAXBContext
;
import
javax
.
xml
.
bind
.
JAXBException
;
import
javax
.
xml
.
bind
.
Marshaller
;
import
javax
.
xml
.
bind
.
Unmarshaller
;
/**
* Object<-->XML转换类
*
* @param <T> 对应的类
*/
public
class
JAXBUtil
<T>
{
/**
* 对象转换为xml
*
* @param element
* @return
* @throws JAXBException
* @throws IOException
*/
public
String
marshal
(
T
element
)
throws
JAXBException
,
IOException
{
ByteArrayOutputStream
os
=
new
ByteArrayOutputStream
(
)
;
try
{
JAXBContext
jc
=
JAXBContext
.
newInstance
(
element
.
getClass
(
)
)
;
Marshaller
m
=
jc
.
createMarshaller
(
)
;
m
.
setProperty
(
Marshaller
.
JAXB_FORMATTED_OUTPUT
,
true
)
;
m
.
marshal
(
element
,
os
)
;
String
xml
=
new
String
(
os
.
toByteArray
(
)
,
"UTF-8"
)
;
return
xml
;
}
finally
{
os
.
close
(
)
;
}
}
@SuppressWarnings
(
{
"unchecked"
,
"rawtypes"
}
)
public
T
unmarshal
(
Class
c
,
String
xml
)
throws
JAXBException
{
JAXBContext
context
;
context
=
JAXBContext
.
newInstance
(
c
)
;
Unmarshaller
unmarshal
=
context
.
createUnmarshaller
(
)
;
T
obj
=
(
T
)
unmarshal
.
unmarshal
(
new
StringReader
(
xml
)
)
;
return
obj
;
}
@SuppressWarnings
(
{
"unchecked"
,
"rawtypes"
}
)
public
T
unmarshal
(
Class
c
,
InputStream
is
)
throws
JAXBException
{
JAXBContext
context
;
context
=
JAXBContext
.
newInstance
(
c
)
;
Unmarshaller
unmarshal
=
context
.
createUnmarshaller
(
)
;
T
obj
=
(
T
)
unmarshal
.
unmarshal
(
is
)
;
return
obj
;
}
}
|
上面的这个工具类就可以方便的把一个JAVA转换为一个XML字符串,也可以把一个XML字符串或者一个XML输入流转换成一个JAVA对象了。
调用的时候也很简单。
1
2
3
4
5
6
7
8
9
10
11
|
public
class
JaxbTest
{
@Test
public
void
test
(
)
throws
JAXBException
,
IOException
{
Property
a
=
new
Property
(
)
;
a
.
setKey
(
"testKey"
)
;
a
.
setValue
(
"testValue"
)
;
a
.
setKey
(
"testKey"
)
;
JAXBUtil
<Property>
util
=
new
JAXBUtil
<Property>
(
)
;
System
.
out
.
println
(
util
.
marshal
(
a
)
)
;
}
}
|