使用opencsv解析csv文件并进行ORM映射为对象集合,可以指定分隔符,主要有以下几种映射方式。
| 接口 | 策略 | 
|---|---|
| MappingStrategy | 顶级接口 | 
| HeaderColumnNameMappingStrategy | 基于DTO属性名或注解与csv头进行映射 | 
| ColumnPositionMappingStrategy | 基于DTO属性数组顺序或注解指定顺序进行映射 | 
| HeaderColumnNameTranslateMappingStrategy | 基于csv头和DTO属性的map关系进行映射 | 
1、Maven导包
<dependency><groupId>com.opencsv</groupId><artifactId>opencsv</artifactId><version>4.6</version></dependency>
2、HeaderColumnNameMappingStrategy
id,short_name,name,remark,parent_id,type_name,type_id1,,大型汽车号牌,1.00,,号牌种类,12,,小型汽车号牌,2.00,,号牌种类,13,,使馆汽车号牌,3.50,,号牌种类,14,,领馆汽车号牌,,,号牌种类,15,,境外汽车号牌,,,号牌种类,16,,外籍汽车号牌,,,号牌种类,17,,普通摩托车号牌,,,号牌种类,18,,低速车号牌,,,号牌种类,19,,拖拉机号牌,,,号牌种类,110,,挂车号牌,,,号牌种类,111,,教练汽车号牌,,,号牌种类,112,,临时行驶车号牌,,,号牌种类,113,,警用汽车号牌,,,号牌种类,114,,重型普通半挂车,20,,车辆类型,215,,重型厢式半挂车,100,,车辆类型,2
@Datapublic class CarCsvDTOByName {@CsvBindByName(column = "id")private String id;@CsvBindByName(column = "short_name")private String shortName;@CsvBindByName(column = "name")private String name;@CsvBindByName(column = "remark")private String remark;@CsvBindByName(column = "parent_id")private String parentId;@CsvBindByName(column = "type_name")private String typeName;@CsvBindByName(column = "type_id")private String typeId;}
@PostMapping("/parseByName")public List parseByName(MultipartFile file) throws IOException {InputStreamReader inputStream = new InputStreamReader(file.getInputStream(), CharsetUtil.CHARSET_GBK);// 设置解析策略,csv的头和POJO属性的名称对应,也可以使用@CsvBindByName注解来指定名称HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();strategy.setType(CarCsvDTOByName.class);CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
- 默认基于DTO类的属性名和csv文件第一行的header进行映射。
 - 
3、ColumnPositionMappingStrategy
1,,大型汽车号牌,1.00,,号牌种类,12,,小型汽车号牌,2.00,,号牌种类,13,,使馆汽车号牌,3.50,,号牌种类,14,,领馆汽车号牌,,,号牌种类,15,,境外汽车号牌,,,号牌种类,16,,外籍汽车号牌,,,号牌种类,17,,普通摩托车号牌,,,号牌种类,18,,低速车号牌,,,号牌种类,19,,拖拉机号牌,,,号牌种类,110,,挂车号牌,,,号牌种类,111,,教练汽车号牌,,,号牌种类,112,,临时行驶车号牌,,,号牌种类,113,,警用汽车号牌,,,号牌种类,114,,重型普通半挂车,20,,车辆类型,215,,重型厢式半挂车,100,,车辆类型,2
3.1、基于@CsvBindByPosition注解方式的DTO
@Datapublic class CarCsvDTOByPosition {@CsvBindByPosition(position = 0)private String id;@CsvBindByPosition(position = 1)private String shortName;@CsvBindByPosition(position = 2)private String name;@CsvBindByPosition(position = 3)private String remark;@CsvBindByPosition(position = 4)private String parentId;@CsvBindByPosition(position = 5)private String typeName;@CsvBindByPosition(position = 6)private String typeId;}
@PostMapping("/parseByPosition")public List parseByPosition(String filePath) throws IOException {InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);// 设置解析策略,使用@CsvBindByPosition注解可以指定字段在csv文件头中的位置,从0开始ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();strategy.setType(CarCsvDTOByPosition.class);CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
3.2、自定义头数组(DTO不需要注解)
@Datapublic class CarCsvDTOByMappingArray {private String id;private String shortName;private String name;private String remark;private String parentId;private String typeName;private String typeId;}
@PostMapping("/parseByMappingArray")public List parseByMappingArray(String filePath) throws IOException {InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);// 设置解析策略,csv文件不需要头,由程序指定ColumnPositionMappingStrategy strategy = new ColumnPositionMappingStrategy();strategy.setType(CarCsvDTOByMappingArray.class);String headers = "id|shortName|name|remark|parentId|typeName|typeId";String[] headerArr = headers.split("\\|");strategy.setColumnMapping(headerArr);CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
 基于@CsvBindByPosition注解指定字段位置来进行映射。
- 也可以通过自定义头数组与csv文件内容按顺序进行映射。
 - 
4、HeaderColumnNameTranslateMappingStrategy
id,short_name,name,remark,parent_id,type_name,type_id1,,大型汽车号牌,1.00,,号牌种类,12,,小型汽车号牌,2.00,,号牌种类,13,,使馆汽车号牌,3.50,,号牌种类,14,,领馆汽车号牌,,,号牌种类,15,,境外汽车号牌,,,号牌种类,16,,外籍汽车号牌,,,号牌种类,17,,普通摩托车号牌,,,号牌种类,18,,低速车号牌,,,号牌种类,19,,拖拉机号牌,,,号牌种类,110,,挂车号牌,,,号牌种类,111,,教练汽车号牌,,,号牌种类,112,,临时行驶车号牌,,,号牌种类,113,,警用汽车号牌,,,号牌种类,114,,重型普通半挂车,20,,车辆类型,215,,重型厢式半挂车,100,,车辆类型,2
@Datapublic class CarCsvDTOByTranslate {private String id;private String shortName;private String name;private String remark;private String parentId;private String typeName;private String typeId;}
@PostMapping("/parseByMappingByTranslate")public List parseByMappingByTranslate(String filePath) throws IOException {InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);// 设置解析策略,key-csv的头、value-DTO属性HeaderColumnNameTranslateMappingStrategy strategy = new HeaderColumnNameTranslateMappingStrategy();strategy.setType(CarCsvDTOByTranslate.class);Map<String, String> columnMapping = new HashMap<>();columnMapping.put("id", "id");columnMapping.put("short_name", "shortName");columnMapping.put("name", "name");columnMapping.put("remark", "remark");columnMapping.put("parent_id", "parentId");columnMapping.put("type_name", "typeName");columnMapping.put("type_id", "typeId");strategy.setColumnMapping(columnMapping);CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
5、字段类型转换和必输项校验
下面演示将字符串转为BigDecimal类型,如果解析时id字段为空则抛异常。
id,short_name,name,remark,parent_id,type_name,type_id1,,大型汽车号牌,1.00,,号牌种类,12,,小型汽车号牌,2.00,,号牌种类,13,,使馆汽车号牌,3.50,,号牌种类,14,,领馆汽车号牌,,,号牌种类,15,,境外汽车号牌,,,号牌种类,16,,外籍汽车号牌,,,号牌种类,17,,普通摩托车号牌,,,号牌种类,18,,低速车号牌,,,号牌种类,19,,拖拉机号牌,,,号牌种类,110,,挂车号牌,,,号牌种类,111,,教练汽车号牌,,,号牌种类,112,,临时行驶车号牌,,,号牌种类,113,,警用汽车号牌,,,号牌种类,114,,重型普通半挂车,20,,车辆类型,215,,重型厢式半挂车,100,,车辆类型,2
@Datapublic class CarCsvDTOConvertAndValid {@CsvBindByName(column = "id", required = true)private String id;@CsvBindByName(column = "short_name")private String shortName;@CsvBindByName(column = "name")private String name;@CsvCustomBindByName(column = "remark", converter = ConvertToBigDecimal.class)private BigDecimal remark;@CsvBindByName(column = "parent_id")private String parentId;@CsvBindByName(column = "type_name")private String typeName;@CsvBindByName(column = "type_id")private String typeId;}
public class ConvertToBigDecimal extends AbstractBeanField {@Overrideprotected Object convert(String value) {if(StringUtils.isNotBlank(value)) {return new BigDecimal(value);}return new BigDecimal(0);}}
@PostMapping("/convertAndValid")public List convertAndValid(String filePath) throws IOException {InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();strategy.setType(CarCsvDTOConvertAndValid.class);// 校验必输项以及做类型转换CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
 默认的解析规则只能支持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
```python@Datapublic class CarCsvDTOConvertAndValid {@CsvBindByName(column = "id", required = true)private String id;@CsvBindByName(column = "short_name")private String shortName;@CsvBindByName(column = "name")private String name;@CsvCustomBindByName(column = "remark", converter = ConvertToBigDecimal.class)private BigDecimal remark;@CsvBindByName(column = "parent_id")private String parentId;@CsvBindByName(column = "type_name")private String typeName;@CsvBindByName(column = "type_id")private String typeId;}
@PostMapping("/parseBySelf")public List parseBySelf(String filePath) throws IOException {InputStreamReader inputStream = new InputStreamReader(new FileInputStream(filePath), CharsetUtil.CHARSET_GBK);HeaderColumnNameMappingStrategy strategy = new HeaderColumnNameMappingStrategy();strategy.setType(CarCsvDTOConvertAndValid.class);CsvToBean csvToBean = new CsvToBeanBuilder(inputStream).withSkipLines(2) // 跳过行数.withSeparator('|') // 分隔符.withFilter(new SkipLineFilter()).withThrowExceptions(false) // 如果有必输项没有,则不抛异常忽略此行.withMappingStrategy(strategy).build();List carCsvDTOList = csvToBean.parse();return carCsvDTOList;}
跳过特定行
public class SkipLineFilter implements CsvToBeanFilter {@Overridepublic boolean allowLine(String[] line) {// 首列为空的行过滤掉return StringUtils.isNotBlank(line[0]);}}
