最近做webService报文转换的公共接口使用到了XSream工具库,写个小总结备忘。。。
XStream是一个可以将javaBean与XML双向转换的java类库,本文内容基于xstream-1.4.4版本。所需maven依赖如下:
1
2
3
4
5
|
<
dependency
>
<
groupId
>com.thoughtworks.xstream</
groupId
>
<
artifactId
>xstream</
artifactId
>
<
version
>1.4.4</
version
>
</
dependency
>
|
1.基本使用方法
new Xstream().toXML(bean);
使用该方式转换的xml格式如下:
1
2
3
4
|
<
org.xstream.test.People
>
<
age
>10</
age
>
<
name
>sedric</
name
>
</
org.xstream.test.People
>
|
该格式由完整类名作为根节点,类成员变量名及变量值组成xml元素内容。
2.定义xml元素名、属性名别名
普通方式:
1
2
3
4
5
6
|
xstream.useAttributeFor(People.
class
,
"age"
);
xstream.useAttributeFor(People.
class
,
"name"
);
//定义Class别名
xstream.alias(
"People"
, People.
class
);
//定义Field别名
xstream.aliasField(
"Age_Alias"
, People.
class
,
"age"
);
|
使用该方式转换的xml格式如下:
1
|
<
People
Age__Alias="10" name="sedric"/>
|
注解方式:
1
2
3
4
5
6
7
8
9
|
@XStreamAlias
(
"People"
)
public
class
People {
@XStreamAlias
(
"Age_Alias"
)
@XStreamAsAttribute
private
int
age;
@XStreamAsAttribute
private
String name;
|
使用注解方式时必须指定xstream解析annotations,方式如下:
1
2
3
4
|
//指定所有class均解析annotations
xstream.autodetectAnnotations(
true
);
//指定指定class解析annotations
xstream.processAnnotations(People.
class
);
|
3.xstream定义别名单下划线(_)双下划线(__)
xstream默认的转换方式中定义了对特殊字符的转换,代码如下:
1
2
3
4
5
6
7
|
//XmlFriendlyNameCoder.encodeName(String name)
for
(; i < length; i++ ) {
char
c = name.charAt(i);
if
(c ==
'$'
|| c ==
'_'
|| c <=
27
|| c >=
127
) {
break
;
}
}
|
xstream对以上范围字符进行特殊字符转换,导致单下划线变为双下划线。解决方式:
方法1:str.replaceAll(“__“,“_“);
方法2:
1
2
3
4
|
//使用xstream自带的NoNameCoder构造xstream,该方式将导致所有特殊字符都不转义
XStream xstream =
new
XStream(
new
XppDriver(
new
NoNameCoder()));
//使用Domdriver及NonameCoder构造xstream,该方式可以指定xml编码方式
XStream xstream =
new
XStream(
new
DomDriver(
"UTF-8"
,
new
NoNameCoder()));
|
方法3:自定义NameCoder,对指定特殊字符进行转换。
4.XStreamImplicit注解使用
当需要将collection或map类型的成员变量中数据转换成xml相同层次的元素时,可以在该成员变量使用该注解。
1
2
|
@XStreamImplicit
(itemFieldName =
"List_Element"
)
private
List<JP> jps;
|
转换的xml如下:
1
2
3
4
5
6
7
8
9
10
|
<
People
Age_Alias="10" name="sedric">
<
List_Element
>
<
gender
>man</
gender
>
<
reason
>人傻钱多</
reason
>
</
List_Element
>
<
List_Element
>
<
gender
>woman</
gender
>
<
reason
>巧言令色</
reason
>
</
List_Element
>
</
People
>
|
XStreamImplicit注解有两个属性:itemFieldName是指当前集合数据转换为xml元素时的 elementName;keyFieldName在集合元素为复杂对象时,会使用集合元素的成员变量名作为元素的elementName,当集合元素为 基本数据类型及String类型时,keyFieldName指定的值将作为元素的elementName。
1
2
3
4
5
6
7
8
|
@XStreamImplicit
(itemFieldName =
"String_Element"
, keyFieldName =
"String_Field"
)
private
List<String> stringList;
@XStreamImplicit
(itemFieldName =
"List_Element"
, keyFieldName =
"List_Field"
)
private
List<JP> jps;
@XStreamImplicit
(itemFieldName =
"Map_Element"
, keyFieldName =
"Map_Field"
)
private
Map<String, String> map;
|
转换的xml如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<
People
Age_Alias="10" name="sedric">
<
String_Element
>str1</
String_Element
>
<
String_Element
>str2</
String_Element
>
<
List_Element
>
<
gender
>man</
gender
>
<
reason
>人傻钱多</
reason
>
</
List_Element
>
<
List_Element
>
<
gender
>woman</
gender
>
<
reason
>巧言令色</
reason
>
</
List_Element
>
<
Map_Element
>value2</
Map_Element
>
<
Map_Element
>value1</
Map_Element
>
</
People
>
|
5.XStreamConverter注解使用
xstreamConvert用于指定class及Field的converter(转换方式)。
xstreamConvert定义于class时,该class及其所有成员变量(递归,即成员变量是复合对象时该符合对象的成员变量)均 默认使用xstreamConvert指定的converter进行转换(满足converter.canConvert(Class type)时一定会使用指定的converter)。
xstreamConvert定义与field时,该field如果是基本类型或String类型,满足canConvert(Class type)方法时使用该converter进行转换;如果是复合对象,则该对象所有成员变量(递归)满足canConvert(Class type)时使用该converter进行转换。
XStreamConverter注解属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//priority指定converter优先级,即当有多个converter均可转换当前对象时,只使用priority
//最大的converter。
int
priority()
default
XStream.PRIORITY_NORMAL;
//以下属性在指定值时,将会在构造该converter时作为构造方法的参数传入。
Class<?>[] types()
default
{};
String[] strings()
default
{};
byte
[] bytes()
default
{};
char
[] chars()
default
{};
short
[] shorts()
default
{};
int
[] ints()
default
{};
long
[] longs()
default
{};
float
[] floats()
default
{};
double
[] doubles()
default
{};
boolean
[] booleans()
default
{};
|
对于converter构造参数较复杂,上述属性不能满足构造参数时,可以使用xstream.registerConverter(参数。。)或xstream.registerLocalConverter(参数。。)来注册converter。
6.xstream对泛型、接口、超类的支持
对于复合类型对象,xstream默认使用ReflectionConverter来进行对象转换。该converter中定义了当属性定义类型与运行时类型不一致时,将会给该元素添加一个class属性。代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
@XStreamAlias
(
"Generic"
)
public
class
GenericTypeTest {
@XStreamAlias
(
"People"
)
private
People people;
GenericTypeTest generic =
new
GenericTypeTest();
//SB是People的子类,SB中定义成员变量
SB sb =
new
SB(
50
,
"sb"
);
generic.setPeople(sb);
|
转的xml如下:
1
2
3
4
5
6
|
<
Generic
>
<
People
class="org.xstream.test.SB" Age_Alias="50" name="sb">
<
gender
>woman</
gender
>
<
reason
>resson</
reason
>
</
People
>
</
Generic
>
|
当转换xml不需要该class属性时,可以通过自定义converter实现。实现方式参照AbstractReflectionConverter.doMarshal方法,只需注释该方法中如下内容:
1
2
3
4
5
6
7
8
9
10
11
12
|
Class defaultType = mapper.defaultImplementationOf(fieldType);
if
(!actualType.equals(defaultType)) {
String serializedClassName = mapper.serializedClass(actualType);
f (!serializedClassName.equals(mapper.serializedClass(defaultType))) {
// String attributeName =
// mapper.aliasForSystemAttribute("class");
// if (attributeName != null) {
// writer.addAttribute(attributeName,
// serializedClassName);
// }
}
}
|
对于xml转javabean,xstream默认的所有converter均不支持泛型、接口。如果存在超类时,xml中存在子类属性时,转换将出现异常,不包含子类属性时,可转换成功。
相关:XStream实用指南