如何用OpenCSV实现含固定与可变部分的CSV自定义转换?
解决OpenCSV处理CSV固定列+可变键值对列的问题
问题本质
你用@CsvCustomBindByName无效的核心原因是:CSV里的"properties"表头是Excel合并单元格生成的,实际CSV数据中可变部分是成对的多列(property1、值1、property2、值2...),而非单个列。@CsvCustomBindByName只会匹配列名完全一致的单个列,自然找不到对应数据,转换器也就不会触发。
便捷解决方案:自定义MappingStrategy
因为你的固定列用了@CsvBindByPosition,我们可以继承ColumnPositionMappingStrategy,重写populateBean方法,在完成固定字段绑定后,手动处理剩余列并转换成List<ContainerProperty>。
步骤1:调整DTO代码
去掉无效的@CsvCustomBindByName注解,保留properties字段并添加setter方法:
// 原有固定字段的@CsvBindByPosition注解保留 @CsvBindByPosition(position = 12, required = true) private Double localizationStop; @CsvBindByPosition(position = 13, required = false) private Double latitudeStop; @CsvBindByPosition(position = 14, required = false) private Double longitudeStop; // 仅保留字段,去掉原注解 private List<ContainerProperty> properties; // 添加setter方法,供自定义策略调用 public void setProperties(List<ContainerProperty> properties) { this.properties = properties; }
步骤2:自定义MappingStrategy
public class ContainerMappingStrategy<T> extends ColumnPositionMappingStrategy<T> { // 固定列总数:从position0到position14共15列 private static final int FIXED_COLUMN_COUNT = 15; @Override public T populateBean(String[] line) throws CsvBeanIntrospectionException, IllegalAccessException, InstantiationException { // 先让父类完成固定字段的绑定 T bean = super.populateBean(line); // 处理可变键值对部分 List<ContainerProperty> properties = new ArrayList<>(); if (line.length > FIXED_COLUMN_COUNT) { // 从固定列之后的位置开始,每两列一组(键+值) for (int i = FIXED_COLUMN_COUNT; i < line.length; i += 2) { // 避免最后一列只有键没有值的情况 if (i + 1 >= line.length) break; String key = line[i].trim(); String value = line[i + 1].trim(); // 跳过空键的无效数据 if (!key.isEmpty()) { ContainerProperty prop = new ContainerProperty(); prop.setKey(key); prop.setValue(value); properties.add(prop); } } } // 将处理后的properties设置到DTO中 if (bean instanceof YourContainerDTO) { ((YourContainerDTO) bean).setProperties(properties); } return bean; } }
步骤3:使用自定义策略解析CSV
// 初始化自定义策略 ContainerMappingStrategy<YourContainerDTO> strategy = new ContainerMappingStrategy<>(); strategy.setType(YourContainerDTO.class); // 构建CsvToBean并解析 CsvToBean<YourContainerDTO> csvToBean = new CsvToBeanBuilder<YourContainerDTO>(new FileReader("your.csv")) .withStrategy(strategy) .withSeparator(';') // 指定CSV分隔符为分号 .build(); List<YourContainerDTO> resultList = csvToBean.parse();
额外注意事项
- 若固定列数量有变动,需同步调整
FIXED_COLUMN_COUNT的值。 - 代码中处理了可变部分不存在、可变部分最后只剩单个键的边界情况,避免数组越界或无效数据。
- 只要可变部分保持键在前、值在后的成对格式,就能正常解析,无需固定键的顺序。
内容的提问来源于stack exchange,提问作者Stefania
相关产品推荐
相关产品推荐

