更好的方式从Excel文件中读取数据

我必须从一个大约有40列的Excel文件中读取数据,我正在使用列索引逐个读取它。 即:

Cell cell = row.getCell(0); if (!(cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK)) { cell.setCellType(Cell.CELL_TYPE_STRING); // set in setter } 

但是这种方法与Excel文件的结构紧密结合,因为如果在它们之间添加了新列,那么将需要主要代码(列的索引值)。

请build议我从Excel文件中读取数据的有效方法,这应该与Excel的结构松散地耦合,或者如果有任何其他方式可以提供与Java对象的字段的列绑定。

我build议添加一个标题行列信息(即名称)和处理列(即映射到Java对象)。 也许你甚至可以使用reflectionAPI来反序列化对象。 像这样的东西是用来保存Java对象到数据库,我不是很好,但你可以谷歌和检查。

该标题行可以隐藏在XL中。

或者你可以把映射信息放到你的java代码中(不要修改原来的XL文件) – 只需要为row.getCell(0)定义一个数据结构来代替硬编码常量 – 它应该被改变为解释你的元数据XL文件中的列。

换句话说,每个您正在处理的XL文件都会有数据定义,并且每个定义都有一个处理XL文件的通用代码。 你应该有一个以XL文件名和定义文件为参数的例程。

有两个选项:

  • 首先/最后
  • 迭代器

首先/最后

POI提供Sheet.getFirstRowNum()/ getLastRowNum()以便能够从第一行到最后一行,并为单元格提供Row.getFirstCellNum()/ getLastCellNum()。

请注意,如果某些未填充,您仍可能遇到空行/单元格。

迭代器

Sheet和Row都实现了Iterable接口,所以你可以做类似的事情

 for(Row row : sheet) { for(Cell cell : row) { ... 

它允许遍历所有可用的行/单元格,而不会遇到任何空项目。

创build将从Excel中读取每一行的实用程序,并为每一行创build一个自定义java对象。 确保在使用前阅读底部的限制。

ExcelUtils.java:

 import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class ExcelUtils { public static <T> List<T> read(String filePath,Class<T> objClass, Map<String,String> headersToPropertyMap){ try { FileInputStream file = new FileInputStream(new File(filePath)); //Create Workbook instance holding reference to .xlsx file XSSFWorkbook workbook = new XSSFWorkbook(file); XSSFSheet sheet = workbook.getSheetAt(0); Iterator<Row> rowIterator = sheet.iterator(); List<T> retList = new LinkedList<T>(); Constructor<T> constructor =objClass.getConstructor(); Map<Integer,String> columnIndexToProperty = null; if(rowIterator.hasNext()){ Row row = rowIterator.next(); columnIndexToProperty = getCorrespondingColumnIndex(headersToPropertyMap,row); } while (rowIterator.hasNext()) { T obj = constructor.newInstance(); Row row = rowIterator.next(); setObjectFromRow(obj,row,columnIndexToProperty); retList.add(obj); } file.close(); return retList; } catch (Exception e) { e.printStackTrace(); } return new LinkedList<T>(); } private static <T> void setObjectFromRow(T obj, Row row, Map<Integer,String> columnIndexToProperty){ int numColumns = row.getPhysicalNumberOfCells(); for(int i=0;i<numColumns;i++){ Object value = getCellValue(row.getCell(i)); ReflectUtils.set(obj, columnIndexToProperty.get(i), value); } } private static Map<Integer,String> getCorrespondingColumnIndex(Map<String,String> headersToPropertyMap,Row row){ int numColumns = row.getPhysicalNumberOfCells(); Map<Integer,String> columnIndexToProperty = new HashMap<Integer,String>(); for(int i=0;i<numColumns;i++){ Cell cell =row.getCell(i); String header = cell.getStringCellValue(); String property = headersToPropertyMap.get(header); if(property==null) System.out.println("Warning: not able to find property with header: "+header); columnIndexToProperty.put(i, property); } return columnIndexToProperty; } private static Object getCellValue(Cell cell ){ switch (cell.getCellType()) { case Cell.CELL_TYPE_NUMERIC: return cell.getNumericCellValue(); case Cell.CELL_TYPE_STRING: return cell.getStringCellValue(); case Cell.CELL_TYPE_BOOLEAN: return cell.getBooleanCellValue(); } return null; } } 

ReflectUtils.java:

 import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import com.google.common.base.Optional; public class ReflectUtils { public static boolean set(Object object, String fieldName, Object fieldValue) { if(fieldName==null) return false; Class<?> clazz = object.getClass(); while (clazz != null) { try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); Type pt=null; try{ pt = field.getGenericType(); }catch(Exception e){ e.printStackTrace(); } if(pt!=null && pt.getTypeName().equals("com.google.common.base.Optional<java.lang.String>")) field.set(object, Optional.fromNullable(fieldValue)); else if(pt!=null && pt.getTypeName().equals("java.lang.String")) if(fieldValue instanceof Double) field.set(object, String.valueOf(((Double)fieldValue).intValue())); else field.set(object, String.valueOf(fieldValue)); else if(pt!=null && (pt.getTypeName().equals("java.lang.Integer") || pt.getTypeName().equals("int"))) if(fieldValue instanceof Double) field.set(object, ((Double) fieldValue).intValue()); else field.set(object, Integer.parseInt(String.valueOf(fieldValue))); else field.set(object, fieldValue); return true; } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); } catch (Exception e) { throw new IllegalStateException(e); } } return false; } } 

用法:

  Map<String,String> headersToPropertyMap = new HashMap<String,String>(); //The header column name in excel-First, the property you wish to assign the value-firstName headersToPropertyMap.put("First", "firstName"); headersToPropertyMap.put("Last", "lastName"); headersToPropertyMap.put("Email", "email"); headersToPropertyMap.put("orgNodeId", "companyname"); headersToPropertyMap.put("Company Name", "companynameString"); headersToPropertyMap.put("EULA", "eula"); headersToPropertyMap.put("Email Notification", "emailNotification"); return ExcelUtils.read("path to excel file",CUSTOM.class,headersToPropertyMap); 

限制:

  • 只支持Java属性的String,Integer和Boolean。
  • 仅支持来自excel的String,Double和boolean。
  • 必须修改ReflectUtils来添加自己的自定义对象。 例如,我添加到ReflectUtils中的可选对象。