• 基于Java+Selenium的WebUI自动化测试框架(十一)-----读取Excel文件(POI)(1)


      上一篇说了利用JXL的jar包来读取Excel的代码。在Java中,还可以用另外一种jar包来读取Excel的内容,那就是Apache的POI。

      这里和之前一样,需要导入POI的jar包,建议导入这三个:poi-4.0.0.jar,poi-ooxml-4.0.0.jar,poi-ooxml-schemas-4.0.0.jar,

      下载地址:https://mvnrepository.com/search?q=POI

      我们先从最小的概念开始,读取一个Cell,即Excel中一个“格子”的内容。

    private static String getValue(Cell cell) {
            if (null == cell) {
                return "";            
            } else if (cell.getCellTypeEnum() == CellType.BOOLEAN) {
                // 返回布尔类型的值
                return String.valueOf(cell.getBooleanCellValue());
            } else if (cell.getCellTypeEnum() == CellType.NUMERIC) {
                // 返回数值类型的值
                return String.valueOf(cell.getNumericCellValue());
            } else {
                // 返回字符串类型的值
                return String.valueOf(cell.getStringCellValue());
            }
        }

        这里会根据每个格子里的数据类型不同,来获取不同的值。(这里设置了三种,布尔型,数字型,字符串型)

          然后,根据我们来把整个行的内容存入一个List中。

        private static List<Object> getRow(Row xssfRow) {
            List<Object> cells = new ArrayList<Object>();
            if (xssfRow != null) {
                for (short cellNum = 0; cellNum < xssfRow.getLastCellNum(); cellNum++) {
                    Cell xssfCell = xssfRow.getCell(cellNum);
                    cells.add(getValue(xssfCell));
                }
            }
            return cells;
        }

      这里的是从行的第1列开始读,因此,我们在设计Excel表格的时候,需要注意一下。

      不过,在这里我想说的是,我们除了需要读取Excel的内容外,我们还希望“按需读取”。什么意思呢?就是说,我们之前是按照Excel的固有格式或者数据结构来读取内容的,比如我去指定开始/结束行,开始/结束列。

    这样的话,我就必须要知道我要读取的范围是什么。但是,一般来说,我们使用Excel的习惯不是这样的。我们习惯于把某列或某行的数据提取或者过滤出来。举个简单的例子来说:假如一个Excel中有A,B,C三列,我们只想要A,C列的数据而忽略B列,这样的话如果用之前的方法,就会很不方便。另外,如果我们需要一次读取N个Excel文件中的A列和C列,也需要对代码进行重新审视。

      为了能够“按需读取”,我们首先需要设计一下这个“需”。在这里,我们引入一个概念,就是构造器(当然,这个也算是一种简单的javaBean),下面就逐步来分析,怎么实现这些功能。

      按照之前我们对页面元素的定义,我们在这里对Excel里面的内容也进行以下定义,即假如我使用Excel存储页面元素的内容,我应该是以什么样的格式去写。一般来说我想以以下的方式:

      

      在这里,pageName是页面名称,positionName就是我们给想点击的页面元素起的名字,type是寻找方式,sec是等待时间,path是寻找元素的具体路径的值。

      在这个Excel中,第一行的列名为我们定义的页面元素属性名称,从第二行的内容开始,我们需要填写每个页面元素实际的属性值。

      那么在这里我们先做一个记录页面元素属性值的构造器,或者叫Bean

    package webui.bean;
    
    public class positionBean {
        //此处定义的是Excel里面列的名字,必须要一模一样,才能正常读取相应的数据!
        String pageName;
        String positionName;
        String path;
        int sec;
        String type;
        public String getPageName() {
            return pageName;
        }
        public void setPageName(String pageName) {
            this.pageName = pageName;
        }
        public String getPositionName() {
            return positionName;
        }
        public void setPositionName(String positionName) {
            this.positionName = positionName;
        }
        public String getPath() {
            return path;
        }
        public void setPath(String path) {
            this.path = path;
        }
        public int getSec() {
            return sec;
        }
        public void setSec(int sec) {
            this.sec = sec;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        } 
    }

      我们根据页面元素记录的属性,编写了这个构造器后,我们怎么让Excel按照这个构造器的内容来读取数据呢?我们这里需要用到Java中类反射的概念。先看下面一段代码:

    private static Map<String, Method> getSetMethod(Class<?> clz,List<Object> heads) {
            Map<String, Method> map = new HashMap<String, Method>();
            Method[] methods = clz.getMethods();
            for (Object head : heads) {
                for (Method method : methods) {
                    if (method.getName().toLowerCase().equals("set" + head.toString().toLowerCase())
                            && method.getParameterTypes().length == 1) {
                        map.put(head.toString(), method);
                        break;
                    }
                }
            }
            return map;
        }

      这段代码稍微有些抽象,我们需要根据两个参数(泛型Class<?>来指代我们刚才书写的构造器,List<Object> heads来对应写在Excel里面第一行的列名),我这里用实例来说明一下。

      Excel上有页面元素属性的几列数据(参考之前的Excel图片),构造器里是通过方法返回来取得的实际数据的。如果,我们把相应的列名和相应的方法对应起来,这样就可以把数据对应起来了。例如:pageName对应setPageName(String pageName)这个方法。

           Method[] methods = clz.getMethods();  //这一句,实际上是获取这个类的所有公共方法。  

      if (method.getName().toLowerCase().equals("set" + head.toString().toLowerCase())&& method.getParameterTypes().length == 1)

      //这个判定实际上也是一个过滤,也就是寻找来自于这个类当中,由编写构造器时,生成的setter方法。(如果方法的名字与set + head的小写字母相同,且方法的参数类型长度为1,即只有1个参数)

      {map.put(head.toString(), method);}

      //将头名与方法对应放入HashMap中。------>可以理解为(pageName对应setPageName方法)

      这样一来,我们就用一个HashMap把这个关系给对应起来了。

      我们在取得这个对应关系之后,我们需要用Java的反射机制,来调用具体的方法来设置pageName的值。

      首先,由于Method的invoke方法,参数必须是一个底层的Object,所以,我们设计我们这个设置值的方法必须有这几个参数:

      Object obj------>其实这个可以是positionBean这个类一个实例,List<Object> data --------> 这个是读取的Excel的具体数据集合,List<Object> heads -------->这个List是读取列名的集合,Map<String,Method> methods ----->这个就是我们之前获取的列名与方法的对应关系集合。

      来看下面一段代码

    private static void setValue(Object obj, List<Object> data,List<Object> heads, Map<String, Method> methods)throws IllegalArgumentException, IllegalAccessException,InvocationTargetException {
        //在获取了对应关系的HashMap之后,我们要对这个Map进行遍历。
    for (Map.Entry<String, Method> entry : methods.entrySet()) { Object value = ""; int dataIndex = heads.indexOf(entry.getKey());
          //按照当前列的序号小于数据List的长度(例如数据List的长度为5,当前为0~4的情况)
    if (dataIndex < data.size()) {
               //使用一个Object来取得当前项的数据。 value
    = data.get(heads.indexOf(entry.getKey())); }
             //取得HashMap里对应的方法 Method method
    = entry.getValue();
            //取得方法里的第一个参数的类型 Class
    <?> param = method.getParameterTypes()[0];
            //如果参数类型为String
    if (String.class.equals(param)) {
             //方法反射,将具体的值赋给列名所代表的obj。 method.invoke(obj, value);
              //如果参数类型为整数 }
    else if (Integer.class.equals(param) || int.class.equals(param)) {
              //加入判断是否为空字符,因为很多时候Excel里空着就是0的意思
    if(value.toString()==""){ value=0; }
              //方法反射 method.invoke(obj,
    new BigDecimal(value.toString()).intValue()); } else if (Long.class.equals(param) || long.class.equals(param)) { if(value.toString()==""){ value=0; } method.invoke(obj, new BigDecimal(value.toString()).longValue()); } else if (Short.class.equals(param) || short.class.equals(param)) { if(value.toString()==""){ value=0; } method.invoke(obj, new BigDecimal(value.toString()).shortValue()); } else { // Date method.invoke(obj, value); } } }

      关于反射,可能稍微比较难于理解。我再贴一段代码,大家可以是否容易理解。

    public class MethodDemo {
       public static void main(String[] args) 
          throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
          Method[] methods = SampleClass.class.getMethods();
          SampleClass sampleObject = new SampleClass();
          methods[1].invoke(sampleObject, "data");
          System.out.println(methods[0].invoke(sampleObject));
       }
    }
    
    class SampleClass {
       private String sampleField;
    
       public String getSampleField() {
          return sampleField;
       }
    
       public void setSampleField(String sampleField) {
          this.sampleField = sampleField;
       } 
    }

      以上运行的结果为:

    data  

      写了这么多,好像离我们的所想要的功能越来越近了。我们把Excel的列名和构造类中的方法关联,然后读取数据来给他们赋值。下一章我们就具体来“按需读取”Excel吧。

  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/generalli2019/p/11429004.html
Copyright © 2020-2023  润新知