首页 > 基础资料 博客日记
EasyExcel 使用自定义Converter处理
2024-08-23 00:00:06基础资料围观226次
文章EasyExcel 使用自定义Converter处理分享给大家,欢迎收藏Java资料网,专注分享技术知识
EasyExcel 使用自定义Converter
吐槽一下
我最难受的四件事:
- 写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进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: