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

bean 转换成 xml

代码如下:

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
@XmlType(propOrder = { "name", "age", "address" })
@XmlRootElement(name = "User")
@XmlAccessorType(XmlAccessType.FIELD)
public class Bean {
@XmlElement(name = "Name")
private String name;
@XmlElement(name = "Age")
private String age;
@XmlElement(name = "Address")
private String address;
@XmlAttribute(name = "Id")
private String id;

public String getName() {
return name;
}

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

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getId() {
return id;
}

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

定义了一个 Bean 类。里面包含了 JAXB 提供的注解。那么如何把 bean 转换成 xml 呢,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws Exception {
Bean bean = new Bean();
bean.setId("1");
bean.setName("rcx");
bean.setAge("19");
bean.setAddress("beijing");
JAXBContext jaxbContext = JAXBContext.newInstance(Bean.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(bean, System.out);
}

//输出结果
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User Id="1">
<Name>rcx</Name>
<Age>19</Age>
<Address>beijing</Address>
</User>

xml 转换成 bean

1
2
3
4
5
6
public void xml2Obj() throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Bean.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Bean bean = (Bean)unmarshaller.unmarshal(new File("t"));
System.out.println(bean);
}

Annotations

列举一些经常使用的 Annotations:

  • XmlAccessorType: 表示一个元素是否可以被序列化。默认XmlAccessType.PUBLIC_MEMBER,代表每个公共获取方法/设置方法对和每个公共字段将会自动绑定到 XML。
  • XmlAttribute: 本注解是最基础和最常使用的一个。它映射一个Java元素(property, attribute, field) 到一个XML节点属性.
  • XmlElement: 使用name映射一个Java元素到XML节点。
  • XmlRootElement: 这可能是JAXB中使用最多的注解了。它用于映射一个类到XML元素。它基本上是每一个JAXB的入口点。

转换工具类

下面是 springside 对 JAXB 转换的工具类封装,比较简单代码如下:

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springside.modules.utils.Exceptions;
import org.springside.modules.utils.Reflections;

/**
* 使用Jaxb2.0实现XML<->Java Object的Mapper.
*
* 在创建时需要设定所有需要序列化的Root对象的Class.
* 特别支持Root对象是Collection的情形.
*
* @author calvin
*/
public class JaxbMapper {

private static ConcurrentMap<Class, JAXBContext> jaxbContexts = new ConcurrentHashMap<Class, JAXBContext>();

/**
* Java Object->Xml without encoding.
*/
public static String toXml(Object root) {
Class clazz = Reflections.getUserClass(root);
return toXml(root, clazz, null);
}

/**
* Java Object->Xml with encoding.
*/
public static String toXml(Object root, String encoding) {
Class clazz = Reflections.getUserClass(root);
return toXml(root, clazz, encoding);
}

/**
* Java Object->Xml with encoding.
*/
public static String toXml(Object root, Class clazz, String encoding) {
try {
StringWriter writer = new StringWriter();
createMarshaller(clazz, encoding).marshal(root, writer);
return writer.toString();
} catch (JAXBException e) {
throw Exceptions.unchecked(e);
}
}

/**
* Java Collection->Xml without encoding, 特别支持Root Element是Collection的情形.
*/
public static String toXml(Collection<?> root, String rootName, Class clazz) {
return toXml(root, rootName, clazz, null);
}

/**
* Java Collection->Xml with encoding, 特别支持Root Element是Collection的情形.
*/
public static String toXml(Collection<?> root, String rootName, Class clazz, String encoding) {
try {
CollectionWrapper wrapper = new CollectionWrapper();
wrapper.collection = root;

JAXBElement<CollectionWrapper> wrapperElement = new JAXBElement<CollectionWrapper>(new QName(rootName),
CollectionWrapper.class, wrapper);

StringWriter writer = new StringWriter();
createMarshaller(clazz, encoding).marshal(wrapperElement, writer);

return writer.toString();
} catch (JAXBException e) {
throw Exceptions.unchecked(e);
}
}

/**
* Xml->Java Object.
*/
public static <T> T fromXml(String xml, Class<T> clazz) {
try {
StringReader reader = new StringReader(xml);
return (T) createUnmarshaller(clazz).unmarshal(reader);
} catch (JAXBException e) {
throw Exceptions.unchecked(e);
}
}

/**
* 创建Marshaller并设定encoding(可为null).
* 线程不安全,需要每次创建或pooling。
*/
public static Marshaller createMarshaller(Class clazz, String encoding) {
try {
JAXBContext jaxbContext = getJaxbContext(clazz);

Marshaller marshaller = jaxbContext.createMarshaller();

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

if (StringUtils.isNotBlank(encoding)) {
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
}

return marshaller;
} catch (JAXBException e) {
throw Exceptions.unchecked(e);
}
}

/**
* 创建UnMarshaller.
* 线程不安全,需要每次创建或pooling。
*/
public static Unmarshaller createUnmarshaller(Class clazz) {
try {
JAXBContext jaxbContext = getJaxbContext(clazz);
return jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
throw Exceptions.unchecked(e);
}
}

protected static JAXBContext getJaxbContext(Class clazz) {
Validate.notNull(clazz, "'clazz' must not be null");
JAXBContext jaxbContext = jaxbContexts.get(clazz);
if (jaxbContext == null) {
try {
jaxbContext = JAXBContext.newInstance(clazz, CollectionWrapper.class);
jaxbContexts.putIfAbsent(clazz, jaxbContext);
} catch (JAXBException ex) {
throw new RuntimeException("Could not instantiate JAXBContext for class [" + clazz + "]: "
+ ex.getMessage(), ex);
}
}
return jaxbContext;
}

/**
* 封装Root Element 是 Collection的情况.
*/
public static class CollectionWrapper {

@XmlAnyElement
protected Collection<?> collection;
}
}

用到的 Reflections 如下:

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* 反射工具类.
*
* 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*
* @author calvin
*/
public class Reflections {
private static final String SETTER_PREFIX = "set";

private static final String GETTER_PREFIX = "get";

private static final String CGLIB_CLASS_SEPARATOR = "$$";

private static Logger logger = LoggerFactory.getLogger(Reflections.class);

/**
* 调用Getter方法.
*/
public static Object invokeGetter(Object obj, String propertyName) {
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(propertyName);
return invokeMethod(obj, getterMethodName, new Class[] {}, new Object[] {});
}

/**
* 调用Setter方法, 仅匹配方法名。
*/
public static void invokeSetter(Object obj, String propertyName, Object value) {
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(propertyName);
invokeMethodByName(obj, setterMethodName, new Object[] { value });
}

/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
public static Object getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);

if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}

Object result = null;
try {
result = field.get(obj);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}

/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
Field field = getAccessibleField(obj, fieldName);

if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}

try {
field.set(obj, value);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常:{}", e.getMessage());
}
}

/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型,
*/
public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args) {
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}

try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}

/**
* 直接调用对象方法, 无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名,如果有多个同名函数调用第一个。
*/
public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
Method method = getAccessibleMethodByName(obj, methodName);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}

try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}

/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
*
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
} catch (NoSuchFieldException e) {// NOSONAR
// Field不在当前类定义,继续向上转型
}
}
return null;
}

/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
*
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");

for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
}
}
return null;
}

/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名。
*
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");

for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
makeAccessible(method);
return method;
}
}
}
return null;
}

/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}

/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}

/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
* eg.
* public UserDao extends HibernateDao<User>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}

/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*
* 如public UserDao extends HibernateDao<User,Long>
*
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or Object.class if cannot be determined
*/
public static Class getClassGenricType(final Class clazz, final int index) {

Type genType = clazz.getGenericSuperclass();

if (!(genType instanceof ParameterizedType)) {
logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}

Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

if ((index >= params.length) || (index < 0)) {
logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}

return (Class) params[index];
}

public static Class<?> getUserClass(Object instance) {
Validate.notNull(instance, "Instance must not be null");
Class clazz = instance.getClass();
if ((clazz != null) && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superClass = clazz.getSuperclass();
if ((superClass != null) && !Object.class.equals(superClass)) {
return superClass;
}
}
return clazz;

}

/**
* 将反射时的checked exception转换为unchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
if ((e instanceof IllegalAccessException) || (e instanceof IllegalArgumentException)
|| (e instanceof NoSuchMethodException)) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException("Unexpected Checked Exception.", e);
}
}

—EOF—