如何使用CommonsCSV将CSVRecord映射到指定类的实例?
解决CommonsCSV动态映射CSVRecord到指定类实例的问题
方案一:纯反射实现(无额外依赖)
通过Java反射直接操作类的字段,完成CSV记录到目标类实例的映射,无需引入额外工具库:
import org.apache.commons.csv.CSVRecord; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class CsvMapper { public List<Object> mapCsvToInstances(Class<?> targetClass, List<CSVRecord> records) throws Exception { List<Object> instances = new ArrayList<>(); // 获取目标类的无参构造器,需确保类已定义无参构造 Constructor<?> constructor = targetClass.getConstructor(); for (CSVRecord record : records) { Object instance = constructor.newInstance(); // 遍历CSV所有列名,匹配类的字段并赋值 for (String columnName : record.getParser().getHeaderNames()) { try { Field field = targetClass.getDeclaredField(columnName); field.setAccessible(true); // 允许访问私有字段 // 转换CSV字符串值为字段对应类型 Object fieldValue = convertValue(record.get(columnName), field.getType()); field.set(instance, fieldValue); } catch (NoSuchFieldException e) { // 处理CSV列无对应字段的情况,如记录日志 System.err.println("字段不存在: " + columnName); } catch (IllegalAccessException e) { // 处理字段赋值失败的情况 e.printStackTrace(); } } instances.add(instance); // 此处可直接调用数据库持久化方法,如dao.save(instance) } return instances; } // 类型转换工具方法,可根据业务扩展更多类型 private Object convertValue(String strValue, Class<?> targetType) { if (strValue == null || strValue.isBlank()) { return null; } if (targetType == String.class) { return strValue; } else if (targetType == Integer.class || targetType == int.class) { return Integer.parseInt(strValue); } else if (targetType == Long.class || targetType == long.class) { return Long.parseLong(strValue); } else if (targetType == Double.class || targetType == double.class) { return Double.parseDouble(strValue); } // 扩展日期类型示例: // else if (targetType == LocalDate.class) { // return LocalDate.parse(strValue, DateTimeFormatter.ofPattern("yyyy-MM-dd")); // } return strValue; } }
方案二:使用Apache Commons BeanUtils简化操作
借助Apache Commons BeanUtils工具类,自动处理反射和类型转换,减少重复代码:
引入依赖(Maven)
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency>
实现代码
import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.csv.CSVRecord; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class CsvMapper { public List<Object> mapCsvToInstances(Class<?> targetClass, List<CSVRecord> records) throws Exception { List<Object> instances = new ArrayList<>(); Constructor<?> constructor = targetClass.getConstructor(); for (CSVRecord record : records) { Object instance = constructor.newInstance(); // 将CSVRecord转换为Map,适配BeanUtils.populate方法 Map<String, String> recordMap = new HashMap<>(); for (String columnName : record.getParser().getHeaderNames()) { recordMap.put(columnName, record.get(columnName)); } try { // 自动完成属性映射和类型转换 BeanUtils.populate(instance, recordMap); } catch (Exception e) { e.printStackTrace(); } instances.add(instance); // 持久化到数据库 } return instances; } }
关键注意事项
- 目标类必须定义无参构造器,否则反射实例化会失败
- CSV列名需与类的字段名大小写严格匹配,若存在命名差异(如CSV用
first_name,类用firstName),需在转换前添加字段名映射逻辑(如将下划线命名转为驼峰命名) - 类型转换需覆盖业务所需的所有类型,复杂类型(如日期、枚举)需单独处理
- 生产环境中不要直接使用
printStackTrace(),应替换为日志框架记录异常信息
内容的提问来源于stack exchange,提问作者Moaaz Gaballah
相关产品推荐
相关产品推荐

