EasyExcel自定义处理器处理单元格空值转换问题(适用全局设置)

前言:在工作中使用EasyExcel处理复杂的大数据量时,会遇到从数据库查询的数据字段存在空值的问题,分享解决思路和解决方案

一开始查询百度的解决方案是使用EasyExcel的内置转换器进行自定义封装处理,代码如下:

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author edward
 * @className NullConverter
 * @description:
 * @date 2022/6/14 16:51
 **/
public class NullConverter implements Converter<String> {

    /**
     * 回到 Java 中的对象类型
     *
     * @return 支持 Java 类
     */
    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

    /**
     * * 返回 excel 中的对象枚举
     * * @return 支持 {@link Cell DataTypeEnum}
     * */
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 将excel对象转换为Java对象
     *
     * @param cellData
     * Excel 单元格数据。NotNull。
     * @param contentProperty
     * 内容属性。可空。
     * @param globalConfiguration
     * 全局配置。NotNull。
     * @return 要放入 Java 对象的数据
     * @抛出异常
     *             例外。
     */
    @Override
    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return "-".equals(cellData.getStringValue())?null:cellData.getStringValue();
    }

    /**
     * 将 Java 对象转换为 excel 对象
     *
     * @参数值
     * Java 数据.NotNull。
     * @param contentProperty
     * 内容属性。可空。
     * @param globalConfiguration
     * 全局配置。NotNull。
     * @return 数据放入 Excel
     * @抛出异常
     *             例外。
     */
    @Override
    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return new CellData<>(null == value?"-":value);
    }
}

应用:

/**
     * ID
     */
    @ExcelProperty(converter = NullConverter.class)
    private Integer id;

说明:转换主要看convertToExcelData()方法,在这里将null值转换成“-”,但通过转换器的方式有两个弊端:
      1.每个字段都要添加@ExcelProperty(converter = NullConverter.class)代码,如果遇到大量的数据字段去填充处理会增加很多工作量。
      2.转换器仅支持需要被处理的数据字段,也就是适用于从数据库查询出来已有的数据,如日期格式或性别字段做转换时才生效,对于本身有些字段就是null值时并不能进入到该方法,所以这种解决方案不适用

第二种就是从数据库查询出来的数据,每个数据字段都进行处理,但也是大数据量时工作量很大,当时就想过能否用EasyExcel的监听器去监听处理每个Sheet工作表对象,但研究过后发现并不适用这种场景。所以这种解决方案也不适用

第三种就是EasyExcel内置处理器去自定义封装,当时就想既然查询出来的数据都是要填充到每个单元格的,那么每个单元格都需要做判断处理,那EasyExcel有没有提供相对应的Handler呢,于是就有了下面的解决方案,代码如下:

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

import java.util.List;

/**
 * @author edward
 * @className NullWriteHandler
 * @description: 自定义写入处理器(处理空值)
 * @date 2022/6/8 20:24
 **/
public class CustomizeWriteHandler implements CellWriteHandler {

    /**
     * 在创建单元格之前调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * Nullable。不使用表写入为空。
     * @param row
     * @param head
     * Nullable。填充数据且不带head的情况下为null。
     * @param columnIndex
     * @param relativeRowIndex
     * Nullable。填充数据时为null。
     * @param isHead
     * 填充数据时始终为假。
     */
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    /**
     * 创建单元格后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * Nullable。不使用表写入为空。
     * @param cell
     * @param head
     * Nullable。填充数据且不带head的情况下为null。
     * @param relativeRowIndex
     * Nullable。填充数据时为null。
     * @param isHead
     * 填充数据时始终为假。
     */
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }

    /**
     * 单元格数据转换后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * Nullable。不使用表写入为空。
     * @param cell
     * @param head
     * Nullable。填充数据且不带head的情况下为null。
     * @param cellData
     * 可空,添加头时为空。
     * @param relativeRowIndex
     * Nullable。填充数据时为null。
     * @param isHead
     * 填充数据时始终为假。
     */
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    /**
     * 在单元格上的所有操作都完成后调用
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * Nullable。不使用表写入为空。
     * @param cell
     * @param head
     * Nullable。填充数据且不带head的情况下为null。
     * @param cellDataList
     * Nullable。添加header时为null。填充数据时可能有多个。
     * @param relativeRowIndex
     * Nullable。填充数据时为null。
     * @param isHead
     * 填充数据时始终为假。
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        /* 判断是否有进行填充数据的操作 */
        if(!isHead){
            /* 获取当前单元格的类型 */
            switch (cell.getCellType()){
                /* 当为String类型是判断是否为null或者"",是的话统一设置成指定值 */
                case Cell.CELL_TYPE_STRING:
                    String stringCellValue = cell.getStringCellValue();
                    if(null == stringCellValue || "".equals(stringCellValue)){
                        cell.setCellValue("-");
                    }
                    break;
            }
        }
    }
}

说明:前三个默认就行,还是使用最后面的方法,在单元格的所有操作都完成后调用该方法,每个单元格去判断是否为String类型,如果是并且是null值的话在重新设置值到cell对象中,实测问题解决!

应用如下图:这里通过链式调用注册一个处理器作用到一个工作表中,如果遇到多个工作表的话需要对每个工作表进行注册,在全局设置下也能保证对每个工作表和单元格的控制
在这里插入图片描述

当我们使用 EasyExcel 库读取 Excel 文件时,有时会遇到单元的情况。在这种情况下,默认情况下读取的结果将是 `null` 或者字符串 (`""`) ,这取决于具体的数据类型和配置。为了给这些空值赋予默认值,可以通过几种方式进行处理。 ### 方法一:自定义 Converter EasyExcel 支持通过编写自定义转换器(Converter)来控制从 Excel 单元内容到 Java 对象属性之间的映射规则。你可以创建一个实现了 `StringConverter` 接口或其他合适类型的 converter,在其中添加对白值的判断并设置默认值。 #### 步骤: 1. 创建一个新的类继承于 `DefaultConverter<String>` 并重写相应的方法; 2. 在 read 函数里检测传入的内容是否为或仅为白字符,如果是则返回设定好的默认值;如果不是,则正常传递原数据。 3. 注册此转换器到全局或者局部范围内生效。 ```java public class DefaultEmptyValueConverter extends DefaultConverter<String> { @Override protected String convertToJavaData(Cell cell, Type targetType, ReadCellData<?> cellData, GlobalConfiguration globalConfiguration, Map<Integer, CellData<?>> map, AnalysisContext context) { if (cell == null || StringUtils.isBlank(cell.getStringCellValue())) { return "默认值"; // 设置你需要的默认值 } else { return super.convertToJavaData(cell, targetType, cellData, globalConfiguration, map, context); } } } ``` 然后在读取文件前注册这个转换器: ```java // 全局应用 ReadBuilder.read(YourClass.class) .registerConverter(new DefaultEmptyValueConverter()) ... // 局部应用 - 如果你知道列名的话也可以单独针对某一列做特殊处理 ContentProperty contentProperty = new ContentProperty(); contentProperty.setIndex(indexOrName); contentProperty.setFillWithDefaultValue(true); // 注意这里需要结合实际情况调整 ``` ### 方法二:直接在实体类中覆盖 get/set 方法 如果只是想简单地解决某些字段为问题而不涉及到复杂的业务逻辑变换,可以直接修改目标实体 Bean 内的相关 getter 和 setter 方法来进行基本判断: ```java private String fieldName; @Getter(onMethod_={@org.apache.commons.lang3.builder.ToStringExclude()}) @Setter public String getFieldName(){ if(this.fieldName==null||this.fieldName.trim().isEmpty()){ return "默认值"; } return this.fieldName; } public void setFieldName(String fieldName){ this.fieldName=fieldName; } ``` 这种方法适用于不需要额外依赖其他工具包的情况下快速解决问题。 ### 方法三:利用注解 `@Column(value="字段名称",fillStrategy) EasyExcel 还提供了另一种更为简洁的方式——通过注解读写策略。你可以在对应实体类属性上加上如下的注释标记,并指明当遇到空值时应采取何种填充策略及提供的默认文本是什么样的形式。 例如: ```java import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.BaseRowModel; import lombok.Data; @Data public static class DataSheet extends BaseRowModel{ @ExcelProperty("标题") @FillStrategy(FillStrategies.FIXED_VALUE,"默认值") private String title; ... // Other fields and methods omitted. } ``` 注意以上代码中的 `@FillStrategy` 是假设存在的伪代码片段,实际操作时请参考最新版本文档确认支持的功能点以及正确的语法式。 综上所述,选择哪种方式取决于个人偏好和技术背景等因素。对于大多数简单的应用场景来说,通常推荐使用方法二是因为它足够直观而且容易实现;而对于较为复杂的需求,则建议考虑第一种方案来自定义converter以获得更高的灵活性。同时别忘了时刻关注官方最新的功能更新以便能够充分利用新特性提升效率!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值