美文网首页Java web
Java解析csv文件

Java解析csv文件

作者: 砒霜拌辣椒 | 来源:发表于2020-07-27 19:41 被阅读0次

使用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_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
@Data
public 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进行映射。
  • 也可以通过@CsvBindByName注解指定映射字段名。

3、ColumnPositionMappingStrategy

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

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

@Data
public 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不需要注解)

@Data
public 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文件内容按顺序进行映射。
  • csv文件中不需要有文件头,如果有则需要手动跳过。

4、HeaderColumnNameTranslateMappingStrategy

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
@Data
public 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_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
@Data
public 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 {
    @Override
    protected 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、自定义解析

  • 解析时跳过首部指定行数。
  • 指定分隔符。
  • 跳过特定的行不进行解析。
  • 指定必输项校验不通过抛出异常或是忽略不进行解析。
=======================跳过此行=======================
=======================跳过此行=======================
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
@Data
public 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 {
    @Override
    public boolean allowLine(String[] line) {
        // 首列为空的行过滤掉
        return StringUtils.isNotBlank(line[0]);
    }
}

Java生成csv文件

参考链接

代码地址

相关文章

网友评论

    本文标题:Java解析csv文件

    本文链接:https://www.haomeiwen.com/subject/fbjtrktx.html