首页 > 基础资料 博客日记

EasyExcel 使用自定义Converter处理

2024-08-23 00:00:06基础资料围观226

文章EasyExcel 使用自定义Converter处理分享给大家,欢迎收藏Java资料网,专注分享技术知识

吐槽一下

我最难受的四件事:

  • 写Api接口文档
  • 写代码注释
  • 调用三方没有文档的接口
  • 调整没有注释的代码

需求背景

类型处理报错

系统使用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);
            // }
        }
    }
}

文章来源:https://blog.csdn.net/transitory_truth/article/details/140685530
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云