自定义注解灵活解析XML

系统管理员 2022-04-03 05:20 290阅读 0赞

问题提出

对于现在的开发者来说,XML的解析有许多可用的工具,包括将XML转化为javaBean。但是仍然不够灵活,如下。

现有XML原文如下所示

  1. <INFO>
  2. <head>
  3. <token>abcd12345678dcba</token>
  4. <msg_id>2018121236214125</msg_id>
  5. </head>
  6. <body>
  7. <name>张三</name>
  8. <age>25</age>
  9. <msg>你好啊</msg>
  10. </body>
  11. </INFO>

有javaBean如下所示

  1. public class MyBean{
  2. private String token;
  3. private String msgId;
  4. private String name;
  5. private Integer age;
  6. private String msg;
  7. // 省略getter setter
  8. }

欲将上面的XML信息转化为JavaBean仍然是比较难受的。使用现有的大部分工具都无法简单的完成。

一、 通过自定义注解构建映射关系

通过上面的例子我们想要的是:能有一个自定义映射,将XML指定的消息封装到javaBean的指定字段中。

思路如下:

  1. 编写一个自定义的注解,该注解主要使用在javaBean的类或字段上
  2. 该注解需要一个参数,参数用来指定该字段对应的XML上的指定路径值
  3. 这个路径可以使用XPath语法规范,同时也方便我们解析
  4. 编写解析工具类,将XML信息封装到javaBean中
自定义的注解
  1. /** * XPath 路径注解 */
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ ElementType.TYPE, ElementType.FIELD})
  4. @Documented
  5. public @interface XmlPath {
  6. /** * xPath语法路径 */
  7. String value() default "";
  8. }
在javaBean上注解指定映射关系

注意使用的是Xpath语法路径

  1. @XmlPath("//INFO")
  2. public class MyBean {
  3. @XmlPath("/head/token")
  4. private String token;
  5. @XmlPath("/head/msg_id")
  6. private String msgId;
  7. @XmlPath("/body/name")
  8. private String name;
  9. @XmlPath("/body/age")
  10. private Integer age;
  11. @XmlPath("/body/msg")
  12. private String msg;
  13. // 省略getter setter
  14. }

二、通过自定义工具类来解析注解,完成功能

思路如下:

  1. 通过dom4j以及XPath语法来解析XML
  2. 通过反射获取类及字段上的自定义注解信息,每一个字段都能得到一个完整的XPath路径。
  3. 字段到XML路径的映射构建完成,存放到Map
  4. 通过内省和Xpath解析将信息封装到javaBean中

    public static T fromXml(String xml, Class clazz) {

    1. try {
    2. Document document = DocumentHelper.parseText(xml);
    3. return fromXml(document, clazz);
    4. } catch (Exception e) {
    5. throw new RuntimeException("xml转换失败,原文=" + xml, e);
    6. }

    }

    public static T fromXml(Document document, Class clazz) throws Exception {

    1. // 获取BeanInfo实例
    2. BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
    3. // 获取对应bean类的所有JavaBean属性的属性描述符对象
    4. PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    5. T obj = clazz.newInstance();
    6. Map<String, String> pathMap = new HashMap<>(16);
    7. Class<? super T> type = clazz;
    8. // 反射获取所有字段(包括其父类)的XmlPath注解, 并构建映射关系
    9. do {
    10. XmlPath typePath = type.getAnnotation(XmlPath.class);
    11. String typeValue = "";
    12. if (typePath != null) {
    13. typeValue = typePath.value();
    14. }
    15. Field[] fields = type.getDeclaredFields();
    16. for (Field f : fields) {
    17. String fieldName = f.getName();
    18. XmlPath fieldPath = f.getAnnotation(XmlPath.class);
    19. if (fieldPath != null) {
    20. pathMap.put(fieldName, typeValue + fieldPath.value());
    21. }
    22. }
    23. } while ((type = type.getSuperclass()) != null);
    24. // 内省
    25. for (PropertyDescriptor pd : propertyDescriptors) {
    26. String name = pd.getName();
    27. String pathValue = pathMap.get(name);
    28. if (pathValue != null) {
    29. Node node = document.selectSingleNode(pathValue);
    30. if (node != null) {
    31. String fieldValue = node.getStringValue();
    32. pd.getWriteMethod().invoke(obj, fieldValue);
    33. }
    34. }
    35. }
    36. return obj;

    }

三、关于javaBean到XML

上文我们解决了从xml到javaBean的灵活封装。那么javaBean到XML也要灵活转化怎么解决呢?

  • 有一个非常方便的方法,那就是借助Freemarker。
  • 用Freemarker语法把模板定义好,然后渲染就行了。方便快捷又极其灵活。

    public static String renderString(String templateString, Map model) throws Exception {

    1. StringWriter result = new StringWriter();
    2. Template t = new Template("name", templateString, new Configuration(Configuration.VERSION_2_3_23));
    3. t.process(model, result);
    4. return result.toString();

    }

发表评论

表情:
评论列表 (有 0 条评论,290人围观)

还没有评论,来说两句吧...

相关阅读