使用opencsv解析csv文件并进行ORM映射为对象集合,可以指定分隔符,主要有以下几种映射方式。

接口 策略
MappingStrategy 顶级接口
HeaderColumnNameMappingStrategy 基于DTO属性名或注解与csv头进行映射
ColumnPositionMappingStrategy 基于DTO属性数组顺序或注解指定顺序进行映射
HeaderColumnNameTranslateMappingStrategy 基于csv头和DTO属性的map关系进行映射

1、Maven导包

  1. <dependency>
  2. <groupId>com.opencsv</groupId>
  3. <artifactId>opencsv</artifactId>
  4. <version>4.6</version>
  5. </dependency>

2、HeaderColumnNameMappingStrategy

  1. id,short_name,name,remark,parent_id,type_name,type_id
  2. 1,,大型汽车号牌,1.00,,号牌种类,1
  3. 2,,小型汽车号牌,2.00,,号牌种类,1
  4. 3,,使馆汽车号牌,3.50,,号牌种类,1
  5. 4,,领馆汽车号牌,,,号牌种类,1
  6. 5,,境外汽车号牌,,,号牌种类,1
  7. 6,,外籍汽车号牌,,,号牌种类,1
  8. 7,,普通摩托车号牌,,,号牌种类,1
  9. 8,,低速车号牌,,,号牌种类,1
  10. 9,,拖拉机号牌,,,号牌种类,1
  11. 10,,挂车号牌,,,号牌种类,1
  12. 11,,教练汽车号牌,,,号牌种类,1
  13. 12,,临时行驶车号牌,,,号牌种类,1
  14. 13,,警用汽车号牌,,,号牌种类,1
  15. 14,,重型普通半挂车,20,,车辆类型,2
  16. 15,,重型厢式半挂车,100,,车辆类型,2
  1. @Data
  2. public class CarCsvDTOByName {
  3. @CsvBindByName(column = "id")
  4. private String id;
  5. @CsvBindByName(column = "short_name")
  6. private String shortName;
  7. @CsvBindByName(column = "name")
  8. private String name;
  9. @CsvBindByName(column = "remark")
  10. private String remark;
  11. @CsvBindByName(column = "parent_id")
  12. private String parentId;
  13. @CsvBindByName(column = "type_name")
  14. private String typeName;
  15. @CsvBindByName(column = "type_id")
  16. private String typeId;
  17. }
  1. @PostMapping("/parseByName")
  2. public List parseByName(MultipartFile file) throws IOException {
  3. InputStreamReader inputStream = new InputStreamReader(file.getInputStream(), CharsetUtil.CHARSET_GBK);
  4. // 设置解析策略,csv的头和POJO属性的名称对应,也可以使用@CsvBindByName注解来指定名称
  5. HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();
  6. strategy.setType(CarCsvDTOByName.class);
  7. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
  8. .withMappingStrategy(strategy)
  9. .build();
  10. List carCsvDTOList = csvToBean.parse();
  11. return carCsvDTOList;
  12. }
  • 默认基于DTO类的属性名和csv文件第一行的header进行映射。
  • 也可以通过@CsvBindByName注解指定映射字段名。

    3、ColumnPositionMappingStrategy

    1. 1,,大型汽车号牌,1.00,,号牌种类,1
    2. 2,,小型汽车号牌,2.00,,号牌种类,1
    3. 3,,使馆汽车号牌,3.50,,号牌种类,1
    4. 4,,领馆汽车号牌,,,号牌种类,1
    5. 5,,境外汽车号牌,,,号牌种类,1
    6. 6,,外籍汽车号牌,,,号牌种类,1
    7. 7,,普通摩托车号牌,,,号牌种类,1
    8. 8,,低速车号牌,,,号牌种类,1
    9. 9,,拖拉机号牌,,,号牌种类,1
    10. 10,,挂车号牌,,,号牌种类,1
    11. 11,,教练汽车号牌,,,号牌种类,1
    12. 12,,临时行驶车号牌,,,号牌种类,1
    13. 13,,警用汽车号牌,,,号牌种类,1
    14. 14,,重型普通半挂车,20,,车辆类型,2
    15. 15,,重型厢式半挂车,100,,车辆类型,2

    3.1、基于@CsvBindByPosition注解方式的DTO

    1. @Data
    2. public class CarCsvDTOByPosition {
    3. @CsvBindByPosition(position = 0)
    4. private String id;
    5. @CsvBindByPosition(position = 1)
    6. private String shortName;
    7. @CsvBindByPosition(position = 2)
    8. private String name;
    9. @CsvBindByPosition(position = 3)
    10. private String remark;
    11. @CsvBindByPosition(position = 4)
    12. private String parentId;
    13. @CsvBindByPosition(position = 5)
    14. private String typeName;
    15. @CsvBindByPosition(position = 6)
    16. private String typeId;
    17. }
    1. @PostMapping("/parseByPosition")
    2. public List parseByPosition(String filePath) throws IOException {
    3. InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);
    4. // 设置解析策略,使用@CsvBindByPosition注解可以指定字段在csv文件头中的位置,从0开始
    5. ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
    6. strategy.setType(CarCsvDTOByPosition.class);
    7. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
    8. .withMappingStrategy(strategy)
    9. .build();
    10. List carCsvDTOList = csvToBean.parse();
    11. return carCsvDTOList;
    12. }

    3.2、自定义头数组(DTO不需要注解)

    1. @Data
    2. public class CarCsvDTOByMappingArray {
    3. private String id;
    4. private String shortName;
    5. private String name;
    6. private String remark;
    7. private String parentId;
    8. private String typeName;
    9. private String typeId;
    10. }
    1. @PostMapping("/parseByMappingArray")
    2. public List parseByMappingArray(String filePath) throws IOException {
    3. InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);
    4. // 设置解析策略,csv文件不需要头,由程序指定
    5. ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();
    6. strategy.setType(CarCsvDTOByMappingArray.class);
    7. String headers = "id|shortName|name|remark|parentId|typeName|typeId";
    8. String[] headerArr = headers.split("\\|");
    9. strategy.setColumnMapping(headerArr);
    10. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
    11. .withMappingStrategy(strategy)
    12. .build();
    13. List carCsvDTOList = csvToBean.parse();
    14. return carCsvDTOList;
    15. }
  • 基于@CsvBindByPosition注解指定字段位置来进行映射。

  • 也可以通过自定义头数组与csv文件内容按顺序进行映射。
  • csv文件中不需要有文件头,如果有则需要手动跳过。

    4、HeaderColumnNameTranslateMappingStrategy

    1. id,short_name,name,remark,parent_id,type_name,type_id
    2. 1,,大型汽车号牌,1.00,,号牌种类,1
    3. 2,,小型汽车号牌,2.00,,号牌种类,1
    4. 3,,使馆汽车号牌,3.50,,号牌种类,1
    5. 4,,领馆汽车号牌,,,号牌种类,1
    6. 5,,境外汽车号牌,,,号牌种类,1
    7. 6,,外籍汽车号牌,,,号牌种类,1
    8. 7,,普通摩托车号牌,,,号牌种类,1
    9. 8,,低速车号牌,,,号牌种类,1
    10. 9,,拖拉机号牌,,,号牌种类,1
    11. 10,,挂车号牌,,,号牌种类,1
    12. 11,,教练汽车号牌,,,号牌种类,1
    13. 12,,临时行驶车号牌,,,号牌种类,1
    14. 13,,警用汽车号牌,,,号牌种类,1
    15. 14,,重型普通半挂车,20,,车辆类型,2
    16. 15,,重型厢式半挂车,100,,车辆类型,2
    1. @Data
    2. public class CarCsvDTOByTranslate {
    3. private String id;
    4. private String shortName;
    5. private String name;
    6. private String remark;
    7. private String parentId;
    8. private String typeName;
    9. private String typeId;
    10. }
    1. @PostMapping("/parseByMappingByTranslate")
    2. public List parseByMappingByTranslate(String filePath) throws IOException {
    3. InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);
    4. // 设置解析策略,key-csv的头、value-DTO属性
    5. HeaderColumnNameTranslateMappingStrategy strategy = new HeaderColumnNameTranslateMappingStrategy();
    6. strategy.setType(CarCsvDTOByTranslate.class);
    7. Map<String, String> columnMapping = new HashMap<>();
    8. columnMapping.put("id", "id");
    9. columnMapping.put("short_name", "shortName");
    10. columnMapping.put("name", "name");
    11. columnMapping.put("remark", "remark");
    12. columnMapping.put("parent_id", "parentId");
    13. columnMapping.put("type_name", "typeName");
    14. columnMapping.put("type_id", "typeId");
    15. strategy.setColumnMapping(columnMapping);
    16. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
    17. .withMappingStrategy(strategy)
    18. .build();
    19. List carCsvDTOList = csvToBean.parse();
    20. return carCsvDTOList;
    21. }

    5、字段类型转换和必输项校验

    下面演示将字符串转为BigDecimal类型,如果解析时id字段为空则抛异常。

    1. id,short_name,name,remark,parent_id,type_name,type_id
    2. 1,,大型汽车号牌,1.00,,号牌种类,1
    3. 2,,小型汽车号牌,2.00,,号牌种类,1
    4. 3,,使馆汽车号牌,3.50,,号牌种类,1
    5. 4,,领馆汽车号牌,,,号牌种类,1
    6. 5,,境外汽车号牌,,,号牌种类,1
    7. 6,,外籍汽车号牌,,,号牌种类,1
    8. 7,,普通摩托车号牌,,,号牌种类,1
    9. 8,,低速车号牌,,,号牌种类,1
    10. 9,,拖拉机号牌,,,号牌种类,1
    11. 10,,挂车号牌,,,号牌种类,1
    12. 11,,教练汽车号牌,,,号牌种类,1
    13. 12,,临时行驶车号牌,,,号牌种类,1
    14. 13,,警用汽车号牌,,,号牌种类,1
    15. 14,,重型普通半挂车,20,,车辆类型,2
    16. 15,,重型厢式半挂车,100,,车辆类型,2
    1. @Data
    2. public class CarCsvDTOConvertAndValid {
    3. @CsvBindByName(column = "id", required = true)
    4. private String id;
    5. @CsvBindByName(column = "short_name")
    6. private String shortName;
    7. @CsvBindByName(column = "name")
    8. private String name;
    9. @CsvCustomBindByName(column = "remark", converter = ConvertToBigDecimal.class)
    10. private BigDecimal remark;
    11. @CsvBindByName(column = "parent_id")
    12. private String parentId;
    13. @CsvBindByName(column = "type_name")
    14. private String typeName;
    15. @CsvBindByName(column = "type_id")
    16. private String typeId;
    17. }
    1. public class ConvertToBigDecimal extends AbstractBeanField {
    2. @Override
    3. protected Object convert(String value) {
    4. if(StringUtils.isNotBlank(value)) {
    5. return new BigDecimal(value);
    6. }
    7. return new BigDecimal(0);
    8. }
    9. }
    1. @PostMapping("/convertAndValid")
    2. public List convertAndValid(String filePath) throws IOException {
    3. InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);
    4. HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();
    5. strategy.setType(CarCsvDTOConvertAndValid.class);
    6. // 校验必输项以及做类型转换
    7. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
    8. .withMappingStrategy(strategy)
    9. .build();
    10. List carCsvDTOList = csvToBean.parse();
    11. return carCsvDTOList;
    12. }
  • 默认的解析规则只能支持DTO中定义简单类型字段,其它类型可以在注解中指定转换的实现类。

  • 指定转换类型的注解主要有@CsvCustomBindByName和@CsvCustomBindByPosition这2种,分别对应基于字段名的映射和基于字段位置的映射。
  • 同时可以在注解中指定必输项,如果解析时为空则抛出异常。

    6、自定义解析

  • 解析时跳过首部指定行数。

  • 指定分隔符。
  • 跳过特定的行不进行解析。
  • 指定必输项校验不通过抛出异常或是忽略不进行解析。 ```python =======================跳过此行======================= =======================跳过此行======================= id|short_name|name|remark|parent_id|type_name|type_id 1||大型汽车号牌|1.00||号牌种类|1 2||小型汽车号牌|2.00||号牌种类|1 3||使馆汽车号牌|3.50||号牌种类|1

4||领馆汽车号牌|||号牌种类|1 5||境外汽车号牌|||号牌种类|1

6||外籍汽车号牌|||号牌种类|1 7||普通摩托车号牌|||号牌种类|1 8||低速车号牌|||号牌种类|1 9||拖拉机号牌|||号牌种类|1 10||挂车号牌|||号牌种类|1 11||教练汽车号牌|||号牌种类|1 12||临时行驶车号牌|||号牌种类|1 13||警用汽车号牌|||号牌种类|1 14||重型普通半挂车|20||车辆类型|2 15||重型厢式半挂车|100||车辆类型|2

  1. ```python
  2. @Data
  3. public class CarCsvDTOConvertAndValid {
  4. @CsvBindByName(column = "id", required = true)
  5. private String id;
  6. @CsvBindByName(column = "short_name")
  7. private String shortName;
  8. @CsvBindByName(column = "name")
  9. private String name;
  10. @CsvCustomBindByName(column = "remark", converter = ConvertToBigDecimal.class)
  11. private BigDecimal remark;
  12. @CsvBindByName(column = "parent_id")
  13. private String parentId;
  14. @CsvBindByName(column = "type_name")
  15. private String typeName;
  16. @CsvBindByName(column = "type_id")
  17. private String typeId;
  18. }
  1. @PostMapping("/parseBySelf")
  2. public List parseBySelf(String filePath) throws IOException {
  3. InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);
  4. HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();
  5. strategy.setType(CarCsvDTOConvertAndValid.class);
  6. CsvToBean csvToBean = new CsvToBeanBuilder(inputStream)
  7. .withSkipLines(2) // 跳过行数
  8. .withSeparator('|') // 分隔符
  9. .withFilter(new SkipLineFilter())
  10. .withThrowExceptions(false) // 如果有必输项没有,则不抛异常忽略此行
  11. .withMappingStrategy(strategy)
  12. .build();
  13. List carCsvDTOList = csvToBean.parse();
  14. return carCsvDTOList;
  15. }

跳过特定行

  1. public class SkipLineFilter implements CsvToBeanFilter {
  2. @Override
  3. public boolean allowLine(String[] line) {
  4. // 首列为空的行过滤掉
  5. return StringUtils.isNotBlank(line[0]);
  6. }
  7. }

参考链接