使用CsvToBean解析CSV时值为null但行数正确问题求解
问题描述
导入CSV文件获取全量数据时,读取到的记录行数和文件实际行数一致,但所有字段值均为null,涉及代码如下:
@RequestMapping(value = "/upload", method = RequestMethod.POST) public String uploadFile(@RequestParam(value = "file") MultipartFile file){ CsvToBean<Person> csvToBean = new CsvToBeanBuilder(reader) .withType(Person.class) .withIgnoreLeadingWhiteSpace(true) .build(); List<Person> records = csvToBean.parse(); System.out.println(records.size()); if(records.size() ==0) { model.addAttribute("message_file", "Aucune donnée trouvée dans le fichier importé"); model.addAttribute("code_file", "000"); } for(int i = 0; i<records.size(); i++){ System.out.println(records.get(i).getid()); } ..... } Person class : @Entity @Table(name = "Person", schema = "MXP") public class Person implements Serializable{ private static final long serialVersionUID = 1L; @Id @Column(name = "person_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XPerson") private Long id; @Column(name = "PERSON_NAME") private String name; //..getters and setters
故障原因
行数匹配说明CSV行解析逻辑正常,字段全为null是映射环节失败导致,常见触发原因有4个:
- 未给实体类字段加OpenCSV的映射注解:OpenCSV不会自动识别JPA的
@Column注解做列名匹配,没有配置绑定规则的字段不会被赋值。 - getter/setter不符合JavaBean命名规范:代码中调用的
getid()方法名属性首字母未大写,OpenCSV通过反射扫描属性方法时无法匹配到对应字段。 - 未正确初始化Reader对象:代码中直接传入未定义/未初始化的reader,没有从MultipartFile中获取文件输入流构造读取器。
- CSV分隔符配置不匹配:如果CSV文件使用分号等非逗号分隔符,未指定分隔符会导致单行列解析错位,也可能出现字段为空的情况。
修复方案
按以下步骤调整代码即可正常读取数据:
- 引入OpenCSV的绑定注解,根据CSV格式选择映射规则
- 如果CSV带表头,用
@CsvBindByName指定和表头一致的列名 - 如果CSV无表头按列位置排序,用
@CsvBindByPosition指定列序号(从0开始计数)
- 如果CSV带表头,用
- 修正所有字段的getter/setter命名,严格遵循JavaBean规范,属性名对应方法的第二个单词首字母大写,比如id字段的getter必须为
getId()、setter为setId(Long id)。 - 从MultipartFile中获取输入流,正确构造Reader对象,建议显式指定字符集避免乱码。
- 根据CSV实际格式配置分隔符、忽略空行等解析参数。
修复后参考代码
Controller层上传逻辑:
@RequestMapping(value = "/upload", method = RequestMethod.POST) public String uploadFile(@RequestParam(value = "file") MultipartFile file, Model model){ // 用try-with-resources自动关闭流,避免资源泄漏 try (Reader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)) { CsvToBean<Person> csvToBean = new CsvToBeanBuilder<Person>(reader) .withType(Person.class) .withIgnoreLeadingWhiteSpace(true) // 分号分隔的CSV打开下方配置 // .withSeparator(';') .build(); List<Person> records = csvToBean.parse(); System.out.println(records.size()); if(records.size() == 0) { model.addAttribute("message_file", "Aucune donnée trouvée dans le fichier importé"); model.addAttribute("code_file", "000"); } for (Person record : records) { // 调用修正后的getId()方法 System.out.println(record.getId()); } // 其余业务逻辑 } catch (Exception e) { e.printStackTrace(); model.addAttribute("message_file", "文件解析失败:" + e.getMessage()); model.addAttribute("code_file", "001"); } return "对应视图路径"; }
Person实体类修正:
import com.opencsv.bean.CsvBindByName; import java.io.Serializable; @Entity @Table(name = "Person", schema = "MXP") public class Person implements Serializable{ private static final long serialVersionUID = 1L; @Id @Column(name = "person_id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XPerson") // 列名和CSV表头保持完全一致,区分大小写 @CsvBindByName(column = "id") // 如果是无表头按位置映射,替换为下方注解 // @CsvBindByPosition(position = 0) private Long id; @Column(name = "PERSON_NAME") @CsvBindByName(column = "name") // @CsvBindByPosition(position = 1) private String name; // 符合规范的getter/setter public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
提示:如果表头和实体类字段名完全一致,
@CsvBindByName可以不指定column属性,会自动按字段名匹配表头。
内容的提问来源于stack exchange,提问作者crystopher cr
相关产品推荐
相关产品推荐

