[英]Better way to read data from Excel file
我必须从一个大约有 40 列的 Excel 文件中读取数据,我正在使用列索引一一读取它。 IE:
Cell cell = row.getCell(0);
if (!(cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK)) {
cell.setCellType(Cell.CELL_TYPE_STRING);
// set in setter
}
但是这种方法与 Excel 文件的结构紧密结合,因为如果在两者之间添加任何新列,则需要主要代码(列的索引值)。
请建议我从 Excel 文件中读取数据的任何有效方法,该方法应该与 Excel 的结构松散耦合,或者是否有任何其他方式可以提供列与 Java 对象字段的绑定。
我建议添加带有列信息(即名称)的标题行并相应地处理列(即将它们映射到 java 对象)。 也许您甚至可以使用反射 API 来反序列化对象。 类似的东西用于将java对象保存到数据库,我在这里不好,但你可以谷歌并检查。
该标题行可以隐藏在 XL 中。
或者您可以将映射信息放入您的 java 代码中(不修改原始 XL 文件)- 只需为此定义一个数据结构,而不是像row.getCell(0)
的硬编码常量-应该更改它以解释您的元数据XL 文件中的列。
换句话说,您将拥有每个您正在处理的 XL 文件的数据定义,以及一个处理每个定义的 XL 文件的通用代码。 您应该有一个将 XL 文件名和定义文件作为参数的例程。
创建了将从 Excel 读取每一行并为每一行创建自定义 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);
限制:
有两种选择:
POI 提供 Sheet.getFirstRowNum()/getLastRowNum() 以便能够从第一行步进到最后一行,并提供 Row.getFirstCellNum()/getLastCellNum() 用于单元格。
请注意,如果未填充某些行/单元格,您仍然可能会遇到空行/单元格。
Sheet 和 Row 都实现了 Iterable 接口,所以你可以做类似的事情
for(Row row : sheet) {
for(Cell cell : row) {
...
这允许在不遇到任何空项目的情况下遍历所有可用的行/单元格。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.