上一篇说了利用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
这里的是从行的第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 MapgetSetMethod(Class> clz,List heads) { Map map = new HashMap (); 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
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
来看下面一段代码
private static void setValue(Object obj, Listdata,List heads, Map methods)throws IllegalArgumentException, IllegalAccessException,InvocationTargetException {
//在获取了对应关系的HashMap之后,我们要对这个Map进行遍历。 for (Map.Entryentry : 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吧。