Java中EasyExcel使用自定义Converter处理方法详解

 更新时间:2024年08月23日 10:25:16   作者:transitory_truth  
EasyExcel自定义Converter是指在使用EasyExcel进行Excel读写操作时,可以自定义转换器来处理一些不支持的数据类型,这篇文章主要给大家介绍了关于Java中EasyExcel使用自定义Converter处理的相关资料,需要的朋友可以参考下

需求背景

类型处理报错

系统使用EasyExcel作为导入导出,有些类型操作会报转换异常:

com.alibaba.excel.exception.ExcelWriteDataConvertException: Can not find 'Converter' support class XXX.

这个问题的原因是因为找不到指定的converter去处理当前类型,默认的各种Converter去com.alibaba.excel.converters包下查看,或者直接查看Converter的各个实现类。作者主要是需要实现LocalDate的导出,其他类型同理。

枚举字段转换

还有一个场景是,如果这个字段的值本身是固定的枚举code,但是导出时要导出成文字。也可以使用。

其他说明

作者当前使用的easyExcel的版本为3.0.5。某些版本内置类名称不太一致,Api有些调整,不过都大同小异。

查看官方文档时发现其对自定义Converter的描述不太完整,特此记录

代码编写

重写converter

  • 首先要重写converter来完成数据的数据准换
public class SpecClassConverter implements Converter<SpecClass> {}
// 以下为实现方法
Class<?> supportJavaTypeKey();
// 支持数据转换的java类型, 例如: return LocalDate.class/Integer.class/Boolean.class/customClass.class;
CellDataTypeEnum supportExcelTypeKey();
// 支持数据转换的单元格类型, 例如: return CellDataTypeEnum.STRING;
T convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration);
// 将单元格内容转换成java对象;导入时使用 例如: return LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));/return cellData.getStringValue().equals("是") ? 1 : 0; 注意自己处理空指针/空数据问题
T convertToJavaData(ReadConverterContext<?> context);
//  return convertToJavaData(context.getReadCellData(), context.getContentProperty(),context.getAnalysisContext().currentReadHolder().globalConfiguration());
// 估计是旧版本Api,默认实现调用上面方法,所以只重写上面方法就可以了 
WriteCellData<?> convertToExcelData(T value, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration);
// 将java对象导出为指定单元格内容 例如: return new WriteCellData<>((LocalDate)value.format(DateUtil.formatter1));/return new WriteCellData<>((Integer)value == 1 ? "是" : "否"); 更多单元格样式设置请参照官方文档
WriteCellData<?> convertToExcelData(WriteConverterContext<T> context);
//  return convertToExcelData(context.getValue(), context.getContentProperty(),context.getWriteContext().currentWriteHolder().globalConfiguration());
// 估计是旧版本Api,默认实现调用上面方法,所以只重写上面方法就可以了 

使用converter

  • 上述代码只是创建了一个自定义的Converter,如果要使用这个Converter需要指定使用,有两种使用方法:
    • 加在指定字段上,指定该字段使用该Converter,注意类型不要搞错,如果该实体内有该类型字段需要指定多次
        @JsonFormat(pattern = "yyyy-MM-dd")
    	@ExcelProperty(value = "开始时间",converter = SpecClassConverter .class)
    	private LocalDate startTime;
    	
        @JsonFormat(pattern = "yyyy-MM-dd")
    	@ExcelProperty(value = "结束时间",converter = SpecClassConverter .class)
    	private LocalDate endTime;
    
    • 也可以在写入Excel时将该Converter注册到ExcelWriterBuilder中,该实体内所有支持类型都会被SpecConverter转换。
    EasyExcel.write(out, SpecClass.class).registerConverter(new SpecClassConverter());
    // 只是简单的完成数据转换,如果需要记录日志等功能,可以将converter托管给spring成为一个bean再引入使用,converter作为默认bean时注意单例的多线程问题
    
  • 注意:如果字段上指定了converter,ExcelWriterBuilder中也注册了Converter且类型支持,字段上的converter优先级最高,ExcelWriterBuilder中的converter在该字段不执行

进阶使用

在数据转换时可以对单元格格式做操作,比如设置不同的单元格颜色、设置不同的字体、设置不同的字号

对于旧实体的处理 (例如: 数字脱敏)

实体类内字段指定使用Converter

// 代码很简单,重写converter的时候指定处理规则,然后将要脱敏的字段指定使用该Converter
@Override
    public WriteCellData<String> convertToExcelData(String value, ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) throws IOException {
                String s = value.replaceAll("[0123456789]", "*");
        WriteCellData<String> res = new WriteCellData<>(s);
        return res;
    }

旧有实体类改造,实体类太多,原字段没有指定converter,固定了要处理的字段名或者指定了 @ExcelProperty 内的value

// 代码思路是在类上注册这个转换器,因为我们有个系统是提数系统,所有的导出最后统一由一个ExcelWriterBuilder处理,所以这个方式很省力
@Override
    public WriteCellData<String> convertToExcelData(String value, ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) throws IOException {
        WriteCellData<String> res;
        Field field = contentProperty.getField();
        // 固定字段值 为了判断当前字段是否处理,获取当前字段的名称,进行判断
      	if(field.getName().equals("handler")){
            String s = value.replaceAll("[0123456789]", "*");
            res = new WriteCellData<>(s);
        }else{
            res = new WriteCellData<>(value);
        }
		// 字段值不固定,指定了 @ExcelProperty 注解中的value,固定导出列头,利用反射拿到注解
		ExcelProperty annotation = contentProperty.getField().getAnnotation(ExcelProperty.class);
        String resValue = value;
        if (annotation != null) {
            String[] value1 = annotation.value();
            String name = value1[value1.length-1];
            if(name.contains("handler")){
                resValue = value.replaceAll("[0123456789]", "*");
            }
        }
        WriteCellData<String> res = new WriteCellData<>(resValue);
		
        
        return res;
    }

旧有实体类改造,没使用 @ExcelProperty 注解,header是List<List<String>>格式,不确定字段名称,只确定导出header

思路是获取到设置的head列表,根据当前列的索引值获取当前header,判断是否处理,在converter中无法获取当前columnIndex,所以放到了CellWriteHandler中。

// 查看registerWriteHandler方法,发现这个WriteHandler 的调用更像是一个链路,所以不用担心覆盖,可以注册多个CustomWriteHandler 
   public T registerWriteHandler(WriteHandler writeHandler) {
        if (parameter().getCustomWriteHandlerList() == null) {
            parameter().setCustomWriteHandlerList(new ArrayList<WriteHandler>());
        }
        parameter().getCustomWriteHandlerList().add(writeHandler);
        return self();
    }


public class CustomWriterHandler implements CellWriteHandler {
// 重写afterCellDataConverted方法,在数据转换处理后对单元格做操作
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    
        if(!isHead){
            int columnIndex = cell.getColumnIndex();
            List<String> strings = writeSheetHolder.getHead().get(columnIndex);
            String s = strings.get(strings.size() - 1);
            if(s.equals("handler")){
                    String stringValue = cellData.getStringValue();
                    String res = stringValue.replaceAll("[0123456789]", "*");
                    cellData.setStringValue(res);
            }
            
            // 下面这种写法如果碰到自定义Head会空指针,而且这种对数据的处理可以的话尽量放到Converter更好
            
            // if(head.getFieldName().equals("handler")){
            //     String stringValue = cellData.getStringValue();
            //     String s = stringValue.replaceAll("[0123456789]", "*");
            //     cellData.setStringValue(s);
            // }
        }
    }
}

总结 

到此这篇关于Java中EasyExcel使用自定义Converter处理方法的文章就介绍到这了,更多相关EasyExcel自定义Converter内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis SQL运行流程源码详解

    Mybatis SQL运行流程源码详解

    这篇文章主要介绍了Mybatis SQL运行流程源码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Default Methods实例解析

    Default Methods实例解析

    这篇文章主要介绍了Default Methods实例解析,介绍了默认方法的相关问题,以及与普通方法的区别,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • Java Arrays工具类用法详解

    Java Arrays工具类用法详解

    这篇文章主要介绍了Java Arrays工具类用法,结合实例形式分析了java Arrays工具类针对数组元素修改、复制、排序等操作使用技巧与相关注意事项,需要的朋友可以参考下
    2019-05-05
  • Spring Boot + Mybatis-Plus实现多数据源的方法

    Spring Boot + Mybatis-Plus实现多数据源的方法

    这篇文章主要介绍了Spring Boot + Mybatis-Plus实现多数据源的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Java 关于String字符串原理上的问题

    Java 关于String字符串原理上的问题

    字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串,让我们一起来了解它
    2022-04-04
  • Mybatis分页插件使用方法详解

    Mybatis分页插件使用方法详解

    这篇文章主要为大家详细介绍了Mybatis分页插件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • Java Spring 控制反转(IOC)容器详解

    Java Spring 控制反转(IOC)容器详解

    这篇文章主要为大家详细介绍了Spring控制反转IoC入门使用的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • 深入浅析Java 抽象类和接口

    深入浅析Java 抽象类和接口

    这篇文章主要介绍了Java 抽象类和接口的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • java编程中自动拆箱与自动装箱详解

    java编程中自动拆箱与自动装箱详解

    这篇文章主要介绍了java编程中自动拆箱与自动装箱详解,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • JDK8中Optional类巧用之判空操作

    JDK8中Optional类巧用之判空操作

    善用Optional可以使我们代码中很多繁琐、丑陋的设计变得十分优雅,这篇文章主要给大家介绍了JDK8中Optional类巧用之判空的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-08-08

最新评论