目录
- 需求背景
- 类型处理报错
- 枚举字段转换
- 其他说明
- 代码编写
- 重写converter
- 使用converter
- 进阶使用
- 对于旧实体的处理 (例如: 数字脱敏)
- 实体类内字段指定使用Converterhttp://www.devze.com
- 旧有实体类改造,实体类太多,原字段没有指定converter,固定了要处理的字段名或者指定了 @ExcelProperty 内的value
- 旧有实体类改造,没使用 @ExcelProperty 注解,header是List<List<String>>格式,不确定字段名称,只确定导出header
- 总结
需求背景
类型处理报错
系统使用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,默认http://www.devze.com实现调用上面方法,所以只重写上面方法就可以了
使用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再引入使用,converterandroid作为默认bean时注意单例的多线程问题
- 注意:如果字段上指定了converter,ExcelWriterBuilder中也注册了Converter且类型支持,字段上的converter优先级最高,ExcelWriterBuilder中的converter在该字段不执行
进阶使用
在数据转换时可以对单元格格式做操作,比如设置不同的单元格颜色、设置不同的字体、设置不同的字号
对于旧实体的处理 (例如: 数字脱敏)
实体类内字段指定使用Converter
// 代码很简单,重写converter的时候指定处理规则,然后将要脱敏的字段指定使用该Converter @Override publi编程c 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内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论