1. 概述

CSV 是系统间和应用程序之间常见的数据交换格式。一个常见的场景是构建处理这些CSV文件的 Java 应用程序。当我们需要将普通的 Java 对象(Plain Old Java Object,简称 POJO)映射到 CSV 格式时,就需要进行这样的操作。

在这个教程中,我们将学习如何使用自定义位置和标题名称将 POJO 映射到 CSV 格式。

2. OpenCSV 库

OpenCSV 是处理 CSV 文件的流行库。首先,我们需要在项目中添加 Maven 依赖:

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.9</version>
</dependency>

3. 生成输入记录

本文我们将生成示例输入记录,然后将它们转换为 CSV 记录。

3.1. 应用程序记录

应用程序记录是一个简单的 POJO,包含 idnameagecreated_at 字段:

3.2. 应用程序列表

我们将生成一个应用程序列表,稍后将转换为 CSV 格式:

List<Application> applications = List.of(
  new Application("123", "Sam", 34, "2023-08-11"),
  new Application("456", "Tam", 44, "2023-02-11"),
  new Application("890", "Jam", 54, "2023-03-11")
);

4. POJO 映射到 CSV

默认情况下,POJO 映射到 CSV 的过程很简单。我们可以使用 StatefulBeanToCsvBuilder,并定义分隔符和其他配置,然后通过 FilerWriter 写入:

public static void beanToCSVWithDefault(List<Application> applications) throws Exception {
    try (FileWriter writer = new FileWriter("application.csv")) {
      var builder = new StatefulBeanToCsvBuilder<Application>(writer)
        .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
        .withSeparator(',')
        .build();
      
      builder.write(applications);
    }
}

默认映射会将 POJO 映射到 CSV,字段名称按升序排列,但如果希望标题采用自定义格式,这就不适用了。

4.1. 自定义标题策略

如果我们希望输出的 CSV 文件具有自定义标题,我们可以在写入 CSV 文件时定义并使用自定义标题策略。

例如,如果我们希望标题全部小写,可以重写 generateHeader: 方法:

public class CustomCSVWriterStrategy<T> extends HeaderColumnNameMappingStrategy<T> {
    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        String[] header = super.generateHeader(bean);
        return Arrays.stream(header)
          .map(String::toLowerCase)
          .toArray(String[]::new);
    }
}

一旦有了自定义标题映射策略,我们就可以构建 StatefulBeanToCsvBuilder,然后以 CSV 格式将 POJO 写入 CSV 文件:

public static void beanToCSVWithCustomHeaderStrategy(List<Application> applications)
  throws IOException, CsvRequiredFieldEmptyException, CsvDataTypeMismatchException {
    try (FileWriter writer = new FileWriter("application.csv")){
      var mappingStrategy = new CustomCSVWriterStrategy<Application>();
      mappingStrategy.setType(Application.class);

      var builder = new StatefulBeanToCsvBuilder<Application>(writer)
        .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
        .withMappingStrategy(mappingStrategy)
        .build();
      builder.write(applications);
    }
}

4.2. 自定义位置策略

将具有特定定义位置的 CSV 字段写入 CSV 文件也是可能的。 我们只需在应用程序记录属性上添加所需的字段位置注解即可:

public record Application(
    @CsvBindByPosition(position = 0)
    String id,
    @CsvBindByPosition(position = 1)
    String name,
    @CsvBindByPosition(position = 2)
    Integer age,
    @CsvBindByPosition(position = 3)
    String created_at) {}

现在,我们可以使用带有注解的 Application 记录将 POJO 映射到 CSV 格式:

public static void beanToCSVWithCustomPositionStrategy(List<Application> applications) throws Exception {
    try (FileWriter writer = new FileWriter("application3.csv")) {
      var builder = new StatefulBeanToCsvBuilder<Application1>(writer)
        .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
        .build();
     
      builder.write(applications);
   }
}

4.3. 自定义位置策略和标题名称

如果希望在特定位置同时包含 CSV 字段和标题,我们可以使用自定义列定位策略实现。

首先,我们需要在应用程序记录类属性上添加位置和标题名称注解:

public record Application1(
    @CsvBindByName(column = "id", required = true)
    @CsvBindByPosition(position = 1)
    String id,
    @CsvBindByName(column = "name", required = true)
    @CsvBindByPosition(position = 0)
    String name,
    @CsvBindByName(column = "age", required = true)
    @CsvBindByPosition(position = 2)
    Integer age,
    @CsvBindByName(column = "position", required = true)
    @CsvBindByPosition(position = 3)
    String created_at) {}

准备好记录类后,我们需要编写自定义列定位策略。这个策略将包含生成标题和字段位置的逻辑:

public class CustomColumnPositionStrategy<T> extends ColumnPositionMappingStrategy<T> {
    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
        super.generateHeader(bean);
        return super.getColumnMapping();
    }
}

现在,我们已经完成了记录和列定位策略,可以将其用于客户端逻辑中,生成 CSV 文件:

public static void beanToCSVWithCustomHeaderAndPositionStrategy(List<Application1> applications)
  throws IOException, CsvRequiredFieldEmptyException, CsvDataTypeMismatchException {
    try (FileWriter writer = new FileWriter("application4.csv")){
        var mappingStrategy = new CustomColumnPositionStrategy<Application1>();
        mappingStrategy.setType(Application1.class);

        var builder = new StatefulBeanToCsvBuilder<Application1>(writer)
          .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
          .withMappingStrategy(mappingStrategy)
          .build();
        builder.write(applications);
    }
}

5. 总结

在这篇教程中,我们学习了如何将 POJO 转换为 CSV 格式并写入 CSV 文件。我们讨论了包括 CSV 字段标题的方法,并提供了自定义标题和位置策略的例子。

OpenCSV 并没有提供一个内置的解决方案来为 CSV 文件设置自定义位置和标题,但很容易编写自定义策略来将字段和标题放在所需的位置。

如往常一样,示例代码可在 GitHub 上查看