如何使用Apache POI将列数动态增加的Excel文件转换为Java对象
处理动态列Excel转Java对象(Apache POI实现)
嘿,这个场景我太熟了——固定实体类根本扛不住动态增加的列,得换个灵活的思路。下面给你两个实用的方案,都是基于Apache POI来实现的:
方案一:用Map<String, Object>存储每行数据(最简单直接)
这种方式不用定义任何实体类,直接用Map的键对应Excel列名,值对应单元格内容,非常适合快速开发。
核心步骤:
- 先读取Excel的表头行,把所有列名存成一个列表;
- 遍历后续的每一行数据,把每个单元格的值和对应的列名绑定到Map中;
- 处理不同类型的单元格(字符串、数字、日期等),避免类型转换错误。
代码示例:
import org.apache.poi.ss.usermodel.*; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class DynamicExcelReader { public static void main(String[] args) throws Exception { String filePath = "your-dynamic-excel.xlsx"; List<Map<String, Object>> dataList = readDynamicExcel(filePath); // 打印测试 for (Map<String, Object> row : dataList) { System.out.println(row); } } public static List<Map<String, Object>> readDynamicExcel(String filePath) throws Exception { List<Map<String, Object>> result = new ArrayList<>(); Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath)); Sheet sheet = workbook.getSheetAt(0); // 第一步:读取表头列名 Row headerRow = sheet.getRow(0); List<String> columnNames = new ArrayList<>(); for (Cell cell : headerRow) { columnNames.add(cell.getStringCellValue().trim()); } // 第二步:遍历数据行 for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row dataRow = sheet.getRow(i); if (dataRow == null) continue; Map<String, Object> rowMap = new HashMap<>(); for (int j = 0; j < columnNames.size(); j++) { Cell cell = dataRow.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); rowMap.put(columnNames.get(j), getCellValue(cell)); } result.add(rowMap); } workbook.close(); return result; } // 处理不同类型的单元格值 private static Object getCellValue(Cell cell) { switch (cell.getCellType()) { case STRING: return cell.getStringCellValue().trim(); case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue(); } else { // 处理数字,避免变成科学计数法 double numericValue = cell.getNumericCellValue(); if (numericValue == (long) numericValue) { return (long) numericValue; } return numericValue; } case BOOLEAN: return cell.getBooleanCellValue(); case FORMULA: return cell.getCellFormula(); default: return null; } } }
方案二:自定义动态实体类(更贴近面向对象)
如果觉得Map不够“像对象”,可以封装一个通用的DynamicEntity类,内部用Map存储属性,对外提供类似实体类的get/set方法,用起来更顺手。
自定义DynamicEntity类:
import java.util.HashMap; import java.util.Map; public class DynamicEntity { private final Map<String, Object> properties = new HashMap<>(); public void setProperty(String key, Object value) { properties.put(key, value); } public Object getProperty(String key) { return properties.get(key); } // 可以加泛型方法,方便类型转换 @SuppressWarnings("unchecked") public <T> T getProperty(String key, Class<T> clazz) { return (T) properties.get(key); } @Override public String toString() { return properties.toString(); } }
读取Excel并转换为DynamicEntity列表:
只需要把方案一中的Map<String, Object>替换成DynamicEntity即可,修改部分代码:
// 遍历数据行时替换成: DynamicEntity entity = new DynamicEntity(); for (int j = 0; j < columnNames.size(); j++) { Cell cell = dataRow.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); entity.setProperty(columnNames.get(j), getCellValue(cell)); } result.add(entity);
使用示例:
// 获取某个属性并转成指定类型 String name = entity.getProperty("name", String.class); Long empId = entity.getProperty("empId", Long.class);
注意事项
- 要确保Excel的表头行是第一行,如果表头位置变化,记得调整代码中
sheet.getRow(0)的索引; - 处理空行:代码中已经判断
dataRow == null时跳过,避免空指针; - 单元格类型处理:一定要覆盖所有可能的类型(字符串、数字、日期、布尔值等),不然容易出现类型转换异常。
内容的提问来源于stack exchange,提问作者Ankit Singh
相关产品推荐
相关产品推荐

