• dom4j+反射实现bean与xml的相互转换


    由于目前在工作中一直用的dom4j+反射实现bean与xml的相互转换,记录一下,如果有不正确的地方欢迎大家指正~~~

    一、反射机制

      在此工具类中使用到了反射技术,所以提前也看了一些知识点,例如:http://www.51gjie.com/java/82(这里面有大量的例子可以学习)

    二、dom4j

      dom4j会将整个xml加载至内存,并解析成一个document对象,但是可能会造成内存溢出现象。

      Document:表示整个xml文档。文档Document对象是通常被称为DOM树。

      Element:表示一个xml元素。Element对象有方法来操作其子元素,它的文本,属性和名称空间

      Attribute:表示元素的属性。属性有方法来获取和设置属性的值。它有父节点和属性类型。

      Node:代表元素,属性或者处理指令。

    三、dom4j读取xml

      读取xml文档主要依赖于org.dom4j.io包,翻看其中源码可以看出提供DOMReader、SaxReader、XPPReader、XPP3Reader,我这里主要查看了SaxReader源码,其他的没有深入看过,所以使用SaxReader。 

        /**
         * 将xml字符串转换为Document对象
         * @param xml
         * @return
         */
        public Document getDocumentByString(String xml)
        {
            //1.字符串输入流
            StringReader stringReader = new StringReader(xml);
            //2.获取解析器
            SAXReader saxReader = new SAXReader();
            //3.声明document对象
            Document document = null;
            try
            {
                //4.解析xml,生成document对象
                document = saxReader.read(stringReader);
            }
            catch (DocumentException e)
            {
                log.error("xml解析失败",e);
            }
            return document;
        }

    四、bean与xml的互转方法

      我这里测试案例,查询学生学校信息,返回学校及多个学生信息。如下准备:

    1.准备bean

    请求实体bean:ReqSchool.java

    public class ReqSchool {
        //学校编号
        private String number;
        //学校名称
        private String name;
        //学校省份
        private String province;
        //学校地址
        private String address;
        //学生班级
        private String stuclass;
        //学生姓名
        private String stuname;
        //学生分数
        private String stuscore;
        //省略set和get方法  
    }

    响应实体bean:RspSchool.java

    public class RspSchool {
        //学校编号
        private String number;
        //学校名称
        private String name;
        //学校省份
        private String province;
        //学校地址
        private String address;
        //多个学生
        private List<Student> students;  //模拟测试数据,返回多个学生
        //省略get和set方法

    响应实体bean的泛型:RspStudent.java

    public class RspStudent {
        //学生班级
        private String stuclass;
        //学生姓名
        private String stuname;
        //学生分数
        private String stuscore;
        //省略set和get方法
    }

    2.准备xml

    ①请求模版requestXML

      这里以${元素名}作为请求模版,也可以修改工具类进行改造。

    <?xml version="1.0" encoding = "GBK"?>
    <SCHOOL>
    <Head>
    <number>${number}</number>
    <name>${name}</name>
    <province>${province}</province>
    <address>${address}</address>
    </Head>
    <Body>
    <stuclass>${stuclass}</stuclass>
    <stuname>${stuname}</stuname>
    <stuscore>${stuscore}</stuscore>
    </Body>
    </SCHOOL>

    ②响应responseXML

    <?xml version="1.0" encoding = "GBK"?>
    <SCHOOL>
    <Head>
    <number>0001</number>
    <name>xxx实验小学</name>
    <province>北京市</province>
    <address>西城区</address>
    </Head>
    <Body>
        <students>
            <student>
                <stuclass>高三二班</stuclass>
                <stuname>李四</stuname>
                <stuscore>100</stuscore>
            </student>
            <student>
                <stuclass>高三三班</stuclass>
                <stuname>张三</stuname>
                <stuscore>95</stuscore>
            </student>
            <student>
                <stuclass>高三四班</stuclass>
                <stuname>王五</stuname>
                <stuscore>0</stuscore>
            </student>
        </students>
    </Body>
    </SCHOOL>

    3.工具类(可直接复制粘贴使用)

      复制粘贴使用时,需保证和我这里的请求报文模版相同(即使用${元素名}),当然也可手动改造此方法。

    @Slf4j
    public class XmlUtil {
        //${abc}正则
        public static String varRegex = "\$\{\s*(\w+)\s*(([\+\-])\s*(\d+)\s*)?\}";
        /**
         * xml解析成document对象
         *
         * @param xml
         * @return
         */
        public  Document getDocument(String xml) {
            StringReader stringReader = new StringReader(xml);
    
            SAXReader saxReader = new SAXReader();
    
            Document document = null;
    
            try {
                document = saxReader.read(stringReader);
            } catch (DocumentException e) {
    
            }
            return document;
        }
    
        /**
         * xml与bean的相互转换
         *
         * @param element
         * @param direction 1:java2xml,2:xml2java
         * @param obj
         */
        public  void parseXml(Element element, String direction, Object obj) {
            //获取当前元素的所有子节点(在此我传入根元素)
            List<Element> elements = element.elements();
            //判断是否有子节点
            if (elements != null && elements.size() > 0)
            {
                //进入if说明有子节点
                //遍历
                for (Element e : elements)
                {
                    //判断转换方向(1:java2xml;2:xml2java)
                    if ("2".equals(direction)) //这里是xml转bean
                    {
                        //声明Field
                        Field field = null;
                        try
                        {
                            //反射获取属性
                            field = obj.getClass().getDeclaredField(e.getName());
                        } catch (Exception e1)
                        {
    
                        }
                        //获取当前属性是否为list
                        if (field!=null&&List.class.getName().equals(field.getType().getName()))
                        {
                            //反射获取set方法
                            Method method = this.getDeclaredMethod(obj, "set".concat(this.toUpperCaseFirstOne(e.getName())), new Class[]{List.class});
                            //声明临时list
                            List temList = new ArrayList();
                            if (method!=null)
                            {
                                try
                                {
                                    //反射调用obj的当前方法,可变参数为templist
                                     method.invoke(obj, temList);
                                } catch (Exception e1) {
                                  log.info("【{}】方法执行失败",method,e1);
                                }
                            }
                            //获取List的泛型参数类型
                            Type gType = field.getGenericType();
                            //判断当前类型是否为参数化泛型
                            if (gType instanceof ParameterizedType)
                            {
                                //转换成ParameterizedType对象
                                ParameterizedType pType = (ParameterizedType) gType;
                                //获得泛型类型的泛型参数(实际类型参数)
                                Type[] tArgs = pType.getActualTypeArguments();
                                if (tArgs!=null&&tArgs.length>0)
                                {
                                    //获取当前元素的所有子元素
                                    List<Element> elementSubList=e.elements();
                                    //遍历
                                    for (Element e1:elementSubList) {
                                        try
                                        {
                                            //反射创建对象
                                            Object tempObj = Class.forName(tArgs[0].getTypeName()).newInstance();
                                            temList.add(tempObj);
                                            //递归调用自身
                                            this.parseXml(e1, direction, tempObj);
                                        } catch (Exception e2)
                                        {
                                            log.error("【{}】对象构造失败",tArgs[0].getTypeName(),e2);
                                        }
                                    }
    
                                }
                            }
                        }
                        else
                        {
                            //说明不是list标签,继续递归调用自身即可
                            this.parseXml(e, direction, obj);
                        }
                    }
                    else if("1".equals(direction))  //说明转换方向为:javabean转xml
                    {
                        //递归调用自身
                        this.parseXml(e, direction, obj);
                    }
                    //此时还在for循环遍历根元素的所有子元素
                }
            }
            else
            {
                //说明无子节点
                //获取当前元素的名称
                String nodeName = element.getName();
                //获取当前元素的对应的值
                String nodeValue = element.getStringValue();
    
                //判断转换方向:1:java2xml、2:xml2java
                if ("1".equals(direction))//java2xml
                {
                    if (nodeValue != null && nodeValue.matches(varRegex))
                    {
                        /**
                         * 获取模板中各节点定义的变量名,例如<traceNo>${traceNo}</traceNo>
                         */
                        nodeValue = nodeValue.substring(nodeValue.indexOf("${") + 2, nodeValue.indexOf("}"));
    
    
                        Object value = null;
                        //根据解析出的变量名,调用obj对象的getXXX()方法获取变量值
                        Method method = this.getDeclaredMethod(obj, "get".concat(this.toUpperCaseFirstOne(nodeValue)), null);
                        if (method != null) {
                            try {
                                value = method.invoke(obj);
                            } catch (Exception e) {
                                log.error("方法【{}】调用异常", "get".concat(this.toUpperCaseFirstOne(nodeValue)));
                            }
                        }
                        //将变量值填充至xml模板变量名位置,例如<traceNo>${traceNo}</traceNo>
                        element.setText(value == null ? "" : value.toString());
                    }
                    //叶子节点
                    log.debug("节点名【{}】,节点变量名【{}】",element.getName(),nodeValue);
                }
                else if ("2".equals(direction))//xml2java
                {
                    if (nodeName != null && !"".equals(nodeName))
                    {
                        //根据xml节点名,调用obj对象的setXXX()方法为obj设置变量值
                        Method method = this.getDeclaredMethod(obj, "set".concat(this.toUpperCaseFirstOne(nodeName)), new Class[]{String.class});
                        if(method!=null)
                        {
                            try
                            {
                                method.invoke(obj, nodeValue);
                            } catch (Exception e)
                            {
                                log.error("方法【{}】调用异常","set".concat(this.toUpperCaseFirstOne(nodeName)));
                            }
                        }
                    }
                }
            }
        }
    
    
        private   Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes)
        {
    
            for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
            {
                try
                {
                    return superClass.getDeclaredMethod(methodName, parameterTypes);
                }
                catch (NoSuchMethodException e)
                {
                    //Method 不在当前类定义, 继续向上转型
                }
                //..
            }
    
            return null;
        }
        private  String toUpperCaseFirstOne(String s)
        {
            // 进行字母的ascii编码前移,效率要高于截取字符串进行转换的操作
            char[] cs = s.toCharArray();
            cs[0] -= 32;
            return String.valueOf(cs);
        }
    }

    4.测试  

    1.准备请求实体bean

        private static ReqSchool makeReq() {
            ReqSchool rspSchool = new ReqSchool();
            //学校编号
            rspSchool.setNumber("1001");
            //学校名称
            rspSchool.setName("实验小学");
            //学校省份
            rspSchool.setProvince("北京市");
            //学校地区
            rspSchool.setAddress("西城区");
            //学生班级
            rspSchool.setStuclass("高一(2)班");
            //学生姓名
            rspSchool.setStuname("张三");
            //学生成绩
            rspSchool.setStuscore("92");
            return rspSchool;
        }

    2.main方法测试(请求)

        public static void  main(String[] args)
        {
              //定义请求模版
              String requestXml="<?xml version="1.0" encoding = "GBK"?>
    " +
                      "<SCHOOL>
    " +
                      "<Head>
    " +
                      "<number>${number}</number>
    " +
                      "<name>${name}</name>
    " +
                      "<province>${province}</province>
    " +
                      "<address>${address}</address>
    " +
                      "</Head>
    " +
                      "<Body>
    " +
                      "<stuclass>${stuclass}</stuclass>
    " +
                      "<stuname>${stuname}</stuname>
    " +
                      "<stuscore>${stuscore}</stuscore>
    " +
                      "</Body>
    " +
                      "</SCHOOL>";
              //这里我直接使用构造方法(实际开发应以线程安全的单例模式)
              XmlUtil xmlUtil = new XmlUtil();
            //获取document对象
            Document document = xmlUtil.getDocument(requestXml);
            //获取根元素
            Element root = document.getRootElement();
            //请求实体bean
            ReqSchool reqSchool = makeReq();
            //解析xml,1:表示java2xml
            xmlUtil.parseXml(root,"1",reqSchool);
            //输出请求报文
            System.out.println(root.asXML());
        }

    查看控制台结果

    3.main方法测试(响应)

    public static void  main(String[] args)
        {
    
              //定义响应报文
            String responseXML="<?xml version="1.0" encoding = "GBK"?>
    " +
                    "<SCHOOL>
    " +
                    "<Head>
    " +
                    "<number>0001</number>
    " +
                    "<name>xxx实验小学</name>
    " +
                    "<province>北京市</province>
    " +
                    "<address>西城区</address>
    " +
                    "</Head>
    " +
                    "<Body>
    " +
                    "    <students>
    " +
                    "        <student>
    " +
                    "            <stuclass>高三二班</stuclass>
    " +
                    "            <stuname>李四</stuname>
    " +
                    "            <stuscore>100</stuscore>
    " +
                    "        </student>
    " +
                    "        <student>
    " +
                    "            <stuclass>高三三班</stuclass>
    " +
                    "            <stuname>张三</stuname>
    " +
                    "            <stuscore>95</stuscore>
    " +
                    "        </student>
    " +
                    "        <student>
    " +
                    "            <stuclass>高三四班</stuclass>
    " +
                    "            <stuname>王五</stuname>
    " +
                    "            <stuscore>0</stuscore>
    " +
                    "        </student>
    " +
                    "    </students>
    " +
                    "</Body>
    " +
                    "</SCHOOL>";
            //这里我直接使用构造方法(实际开发应以线程安全的单例模式)
            XmlUtil xmlUtil = new XmlUtil();
            Document document = xmlUtil.getDocument(responseXML);
            Element rootElement = document.getRootElement();
            RspSchool rspSchool = new RspSchool();
            xmlUtil.parseXml(rootElement,"2",rspSchool);
            System.out.println(rspSchool);
    
        }

    控制结果如下

    五、总结

    1.dom4j解析xml的步骤

    ①获取执行xml的输入流

    ②创建xml读取对象(SaxReader),用于读取输入流

    ③通过读取对象(SaxReader)读取xml的输入流,获取文档对象(Document)

    ④通过文档对象,得到整个文档的 根元素对象(Element)

    ⑤通过根元素,得到其他层次的所有元素对象

    2.反射

      反射这里是重中之重,感谢大家的阅读,如有问题,欢迎大家指正~

  • 相关阅读:
    Spring多数据源动态切换
    IntelliJ Idea使用代码格式化,Tab制表符进行缩进
    idea 快捷键
    final关键字的功能概述
    IntelliJ Idea 常用快捷键列表
    Log4j.properties配置详解
    IDEA添加try catch快捷键
    使用 JMeter 进行压力测试
    idea 复制当前行到下一行快捷键
    js父窗口opener与parent
  • 原文地址:https://www.cnblogs.com/rmxd/p/11365844.html
Copyright © 2020-2023  润新知