自定义注解灵活解析XML
问题提出
对于现在的开发者来说,XML的解析有许多可用的工具,包括将XML转化为javaBean。但是仍然不够灵活,如下。
现有XML原文如下所示
<INFO>
<head>
<token>abcd12345678dcba</token>
<msg_id>2018121236214125</msg_id>
</head>
<body>
<name>张三</name>
<age>25</age>
<msg>你好啊</msg>
</body>
</INFO>
有javaBean如下所示
public class MyBean{
private String token;
private String msgId;
private String name;
private Integer age;
private String msg;
// 省略getter setter
}
欲将上面的XML信息转化为JavaBean仍然是比较难受的。使用现有的大部分工具都无法简单的完成。
一、 通过自定义注解构建映射关系
通过上面的例子我们想要的是:能有一个自定义映射,将XML指定的消息封装到javaBean的指定字段中。
思路如下:
- 编写一个自定义的注解,该注解主要使用在javaBean的类或字段上
- 该注解需要一个参数,参数用来指定该字段对应的XML上的指定路径值
- 这个路径可以使用XPath语法规范,同时也方便我们解析
- 编写解析工具类,将XML信息封装到javaBean中
自定义的注解
/** * XPath 路径注解 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.FIELD})
@Documented
public @interface XmlPath {
/** * xPath语法路径 */
String value() default "";
}
在javaBean上注解指定映射关系
注意使用的是Xpath语法路径
@XmlPath("//INFO")
public class MyBean {
@XmlPath("/head/token")
private String token;
@XmlPath("/head/msg_id")
private String msgId;
@XmlPath("/body/name")
private String name;
@XmlPath("/body/age")
private Integer age;
@XmlPath("/body/msg")
private String msg;
// 省略getter setter
}
二、通过自定义工具类来解析注解,完成功能
思路如下:
- 通过dom4j以及XPath语法来解析XML
- 通过反射获取类及字段上的自定义注解信息,每一个字段都能得到一个完整的XPath路径。
- 字段到XML路径的映射构建完成,存放到Map
通过内省和Xpath解析将信息封装到javaBean中
public static
T fromXml(String xml, Class clazz) { try {
Document document = DocumentHelper.parseText(xml);
return fromXml(document, clazz);
} catch (Exception e) {
throw new RuntimeException("xml转换失败,原文=" + xml, e);
}
}
public static
T fromXml(Document document, Class clazz) throws Exception { // 获取BeanInfo实例
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
// 获取对应bean类的所有JavaBean属性的属性描述符对象
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
T obj = clazz.newInstance();
Map<String, String> pathMap = new HashMap<>(16);
Class<? super T> type = clazz;
// 反射获取所有字段(包括其父类)的XmlPath注解, 并构建映射关系
do {
XmlPath typePath = type.getAnnotation(XmlPath.class);
String typeValue = "";
if (typePath != null) {
typeValue = typePath.value();
}
Field[] fields = type.getDeclaredFields();
for (Field f : fields) {
String fieldName = f.getName();
XmlPath fieldPath = f.getAnnotation(XmlPath.class);
if (fieldPath != null) {
pathMap.put(fieldName, typeValue + fieldPath.value());
}
}
} while ((type = type.getSuperclass()) != null);
// 内省
for (PropertyDescriptor pd : propertyDescriptors) {
String name = pd.getName();
String pathValue = pathMap.get(name);
if (pathValue != null) {
Node node = document.selectSingleNode(pathValue);
if (node != null) {
String fieldValue = node.getStringValue();
pd.getWriteMethod().invoke(obj, fieldValue);
}
}
}
return obj;
}
三、关于javaBean到XML
上文我们解决了从xml到javaBean的灵活封装。那么javaBean到XML也要灵活转化怎么解决呢?
- 有一个非常方便的方法,那就是借助Freemarker。
用Freemarker语法把模板定义好,然后渲染就行了。方便快捷又极其灵活。
public static String renderString(String templateString, Map
model) throws Exception { StringWriter result = new StringWriter();
Template t = new Template("name", templateString, new Configuration(Configuration.VERSION_2_3_23));
t.process(model, result);
return result.toString();
}
还没有评论,来说两句吧...