在java使用Apache poi 操作Excel-基本概念与使用中介绍了poi与excel相关的基本概念,接下来通过poi来导入excel。
本文将使用如下版本的 POI来完成导入数据的演示。
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version> </dependency>由上篇文章可知Excel的基本结构,因此导入Excel就是要将Excel某个电子表单中的所有行读入到程序中。
public static <T> List<T> importExcel(File excelFile) throws InvalidFormatException, IOException, InstantiationException, IllegalaccessException { XSSFWorkbook workBook = new XSSFWorkbook(excelFile); XSSFSheet sheet = workBook.getSheetAt(0); for(int ri = sheet.getFirstRowNum() + 1 ; ri <= sheet.getLastRowNum(); ri++) { Row row = sheet.getRow(ri); for(int ci = row.getFirstCellNum(); ci < row.getLastCellNum(); ci++) { Cell cell = row.getCell(ci); Object value = getCellValue(cell,fields[ci]); System.out.PRintf("%s ",value); } System.out.println(""); } return result; } public static Object getCellValueByType(Cell cell) { int type = cell.getCellType(); switch (type) { case Cell.CELL_TYPE_BLANK: return null; case Cell.CELL_TYPE_BOOLEAN: return cell.getBooleanCellValue(); case Cell.CELL_TYPE_ERROR: return cell.getErrorCellValue(); case Cell.CELL_TYPE_FORMULA: return cell.getNumericCellValue(); case Cell.CELL_TYPE_NUMERIC: return cell.getNumericCellValue(); case Cell.CELL_TYPE_STRING: return cell.getStringCellValue(); default: }如上代码是将excelFile读入,然后读取workBook.getSheetAt(0);表单,接着遍历各个行,并获取各个行的数据并输出。
由于Excel单元格的数据由不同的类型组成,因此获取数据时要判断单元格类型,并调对应的方法获取数据。
接下来将1中读取的数据保存在Java对象中。这里以Person对象为例。Person对象省略了Getter,Setter,及toString方法。
public class Person { private String name; private String sex; private Integer age; private Double height; }使用的Excel数据如下所示。首行是标题列,首行之后是数据列。
姓名 性别 年龄 身高(M)洛克 男 18 1.83 罗琳 女 18 1.76为了将Excel中读取的数据存储在Java对象中,将1中的方法签名修改为,其中type是最终的类型,本文中为Person。
public static <T> List<T> importExcel(File excelFile,Class<T> type)修改后的importExcel方法如下。
public static <T> List<T> importExcel(File excelFile,Class<T> type) throws InvalidFormatException, IOException, InstantiationException, IllegalAccessException { XSSFWorkbook workBook = new XSSFWorkbook(excelFile); List<T> result = new ArrayList<T>(); XSSFSheet sheet = workBook.getSheetAt(0); for(int ri = sheet.getFirstRowNum() + 1 ; ri <= sheet.getLastRowNum(); ri++) { Row row = sheet.getRow(ri); T object = type.newInstance(); Field[] fields = object.getClass().getDeclaredFields(); Field.setAccessible(fields, true); for(int ci = row.getFirstCellNum(); ci < row.getLastCellNum(); ci++) { Cell cell = row.getCell(ci); Object value = getCellValue(cell,fields[ci]); fields[ci].set(object, value); } result.add(object); Field.setAccessible(fields, false); } return result; }上述代码忽略Excel标题行,因此是从首行+1行开始进行遍历。
代码中首先根据type来创建一个实例。 T object = type.newInstance();
注意事项:这里需要注意的是对象的各个成员的类型要与Excel各列所保持一致,这样免去寻找对象成员与Excel列单元的对应关系的麻烦,并简化示例。
创建完实例后,获取对象中的各个字段,并修改访问权限,以便随后赋值。
Field[] fields = object.getClass().getDeclaredFields();Field.setAccessible(fields, true);接着遍历各个列,由于对象中字段的顺序与Excel列顺序保持一致,因此使用同样的列索引即可获取Excel列单元与对象成员。
Person成员的类型由String,int,double,而在Excel中单元格数值的类型统一被描述为Cell.CELL_TYPE_NUMERIC类型,因此在导入并转换为Java对象就需要格外注意类型处理,以避免出错。以本文中Excel中年龄为例,获取后单元格数值使用的是cell.getNumericCellValue()方法,该方法返回类型为Double,而Java对象中使用的是Integer。此外使用中也可能将年龄写成字符串。因此需要对每种数据类型判断并合理转换,或给出相应错误提示。
通过定义接口TypeHandler 来处理各种类型转换的问题。
public interface TypeHandler { public Object handle(Cell cell,Field field);}以Integer类型为例,接着定义Integer处理器。
public class IntegerHandler implements TypeHandler{ @Override public Object handle(Cell cell, Field field) { int type = cell.getCellType(); if(type == Cell.CELL_TYPE_NUMERIC) { return (int)cell.getNumericCellValue(); } else if(type == Cell.CELL_TYPE_STRING) { try { return Integer.valueOf(cell.getStringCellValue()); }catch(NumberFormatException e) { return null; } } else { return null; } }}当单元格类型返回double(Cell.CELL_TYPE_NUMERIC),则执行类型转换。如果是字符串类型,则尝试转换为整型。如果无法转换则返回null。对于无法转换的类型直接返回null。
其它类型如String,Double以处理方法与此一致。
最后修改getCellValue,代码如下。
private final static Map<Class<?>,TypeHandler> typeHandlerMap = new HashMap<>(); static { typeHandlerMap.put(Integer.class, new IntegerHandler()); typeHandlerMap.put(Double.class, new DoubleHandler()); typeHandlerMap.put(String.class, new StringHandler()); } private static Object getCellValue(Cell cell,Field field) { TypeHandler handler = typeHandlerMap.get(field.getType()); if(handler == null) { return null; } return handler.handle(cell, field); }1,2中讲述了基本的导入功能实现。而对字段排序,验证等还可以结合自定义注解来完成,也就是在获取对象Field时,拿到注解并获取相关参数,最后完成校验排序等功能。
使用时要根据实际情况调整或新增handler即可。
新闻热点
疑难解答