简化数据转换:使用 Reflection 将 Excel 和 CSV 转换为 Java 对象

将 Excel 或 CSV 文件转换为 Java 对象 (POJO),反之亦然可能是一个复杂的过程,但使用正确的工具和技术,它变得更加易于管理。在本指南中,我们将探索一个利用 Java 反射的强大 Java 库。

首先,我们将依赖项添加到 Maven 中。

<dependency>
  <groupId>com.adnanebk</groupId>
  <artifactId>excel-csv-converter</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>

了解 POJO 类

在深入研究该库之前,让我们仔细看一下用作数据模型的示例 Java 类:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@SheetDefinition(datePattern = "dd/MM/yyyy")
public class Product {

    @CellDefinition(0)
    private String name;

    @CellDefinition(1)
    private long price;

    @CellDefinition(2)
    @CellBoolean(trueValue = "yes",falseValue = "no")
    private boolean active;

    @CellDefinition(value = 3, title = "Promo price")
    private double promoPrice;

    // Additional fields...

    @CellEnum(enumsMapperMethod = "categoryMap")
    @CellDefinition(10)
    private Category category;

    @CellDefinition(11)
    private LocalDateTime localDateTime;

    private Map<Category,String> categoryMap(){
        return Map.of(Category.A,"Formatted A",
                      Category.B,"Formatted B");
    }
}

此类使用各种注释进行注释,这些注释在转换过程中起着至关重要的作用。每个字段都标有 ,指示其在 Excel 或 CSV 文件中的位置。Product@CellDefinition

我们还可以定义单元格的标题,默认情况下,它会将字段的驼峰大小写名称转换为带空格的名称(例如:firstName=>名字)

批注提供了其他信息,例如将在日期字段类型转换期间使用的日期格式模式。@SheetDefinition

The Enum Annotation: @CellEnum(enumsMapperMethod = “categoryMap”)

在类中,我们使用枚举类别字段中的注释。enumsMapperMethod 参数允许我们定义方法名称;此方法应返回一个映射,该映射定义枚举常量与 Excel/CSV 单元格中的格式化值之间的映射(转换)(默认情况下,将使用枚举常量),请注意,方法名称必须与枚举映射方法参数值非常相似。Product@CellEnum

The Boolean Annotation: @CellBoolean(trueValue = “yes”,falseValue = “no”)

我们在布尔活动字段中使用 @CellBoolean 注释,它有两个参数表示我们要在 Excel/CSV 字段中使用的格式值。

现在,让我们介绍一下 POJO 类的更新版本:ProductV2

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@SheetDefinition(includeAllFields = true,titles={"Name","Category","Date"})
public class ProductV2 {

    private String name;

    // Additional fields...

    private Category category;

    @IgnoreCell
    private LocalDateTime localDateTime;
}

使用类中提供的注释将字段映射到 Excel 文件中的相应单元格,将 Excel 文件转换为 POJO 变得更加简单。@SheetDefinition

当 includeAllFields 参数设置为 true 时,字段将根据其声明的顺序自动包含在单元格中并映射到单元格中,并忽略使用@IgnoreCell批注批注的字段。

我们可以在 titles 参数中定义标题,条件是它们必须与字段的顺序相同。

将 Excel/CSV 转换为 POJO,反之亦然

@RestController
@RequestMapping("excel/products")
public class ExcelFieldsController {
    private final ExcelHelper<Product> excelHelper = ExcelHelper.create(Product.class);

    @GetMapping
    public List<Product> excelToProducts(@RequestBody MultipartFile file){
        return excelHelper.toStream(file.getInputStream()).toList();
    }

       @GetMapping("/download")
    public ResponseEntity<InputStreamResource>
    downloadExcelFromProducts() {
        String filename = "products-" + LocalDate.now() + ".xlsx";
        InputStreamResource file = new InputStreamResource(excelHelper.toExcel(getProducts()));
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
                .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
                .body(file);
    }
}

这同样适用于转换 CSV 文件,只是我们需要定义将使用的分隔符

    private final CsvHelper<ProductV2> csvHelper = CsvHelper.create(ProductV2.class,";");

The Class: Dynamic Class ExaminationReflectionUtil

ReflectionUtil 类是这个 Java 库的主干,通过 Java 反射的强大功能促进动态类检查和操作。ReflectionUtil 类的一个值得注意的功能是应用优化来增强性能。在初始化期间,所有 getter、setter 和字段都会被紧急加载并封装在 SheetField 记录中。这种深思熟虑的操作最大限度地减少了后续操作中对反射查找的需求,并提高了整体效率。

SheetField Record Overview

Field 记录是库的基本组件,旨在封装有关类字段的信息。

public record SheetField<T>(String typeName, String title, Function<T,Object> getter, BiConsumer<T,Object> setter, int cellIndex)

主要方法:

public Object getValue(T obj):使用对象的 getter 方法从对象中检索字段的值。如果字段是枚举,则它根据定义的枚举映射提供格式化值。

public void setValue(T obj, Object value):使用对象的 setter 方法设置对象中字段的值。它处理枚举值并确保正确转换。

结论

通过利用这个自定义库,开发人员可以显着简化在 Java 中将 Excel 和 CSV 文件转换为 POJO 的过程。Java 反射的集成,以及深思熟虑的设计考虑,使动态映射成为数据处理任务的宝贵工具。

GitHub 存储库

https://github.com/adnanebk/excel-pojo-converter