Java中EasyExcel使用自定義Converter處理方法詳解
需求背景
類型處理報錯
系統(tǒng)使用EasyExcel作為導(dǎo)入導(dǎo)出,有些類型操作會報轉(zhuǎn)換異常:
com.alibaba.excel.exception.ExcelWriteDataConvertException: Can not find 'Converter' support class XXX.
這個問題的原因是因為找不到指定的converter去處理當前類型,默認的各種Converter去com.alibaba.excel.converters包下查看,或者直接查看Converter的各個實現(xiàn)類。作者主要是需要實現(xiàn)LocalDate的導(dǎo)出,其他類型同理。
枚舉字段轉(zhuǎn)換
還有一個場景是,如果這個字段的值本身是固定的枚舉code,但是導(dǎo)出時要導(dǎo)出成文字。也可以使用。
其他說明
作者當前使用的easyExcel的版本為3.0.5。某些版本內(nèi)置類名稱不太一致,Api有些調(diào)整,不過都大同小異。
查看官方文檔時發(fā)現(xiàn)其對自定義Converter的描述不太完整,特此記錄
代碼編寫
重寫converter
- 首先要重寫converter來完成數(shù)據(jù)的數(shù)據(jù)準換
public class SpecClassConverter implements Converter<SpecClass> {} // 以下為實現(xiàn)方法 Class<?> supportJavaTypeKey(); // 支持數(shù)據(jù)轉(zhuǎn)換的java類型, 例如: return LocalDate.class/Integer.class/Boolean.class/customClass.class; CellDataTypeEnum supportExcelTypeKey(); // 支持數(shù)據(jù)轉(zhuǎn)換的單元格類型, 例如: return CellDataTypeEnum.STRING; T convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration); // 將單元格內(nèi)容轉(zhuǎn)換成java對象;導(dǎo)入時使用 例如: return LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));/return cellData.getStringValue().equals("是") ? 1 : 0; 注意自己處理空指針/空數(shù)據(jù)問題 T convertToJavaData(ReadConverterContext<?> context); // return convertToJavaData(context.getReadCellData(), context.getContentProperty(),context.getAnalysisContext().currentReadHolder().globalConfiguration()); // 估計是舊版本Api,默認實現(xiàn)調(diào)用上面方法,所以只重寫上面方法就可以了 WriteCellData<?> convertToExcelData(T value, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration); // 將java對象導(dǎo)出為指定單元格內(nèi)容 例如: return new WriteCellData<>((LocalDate)value.format(DateUtil.formatter1));/return new WriteCellData<>((Integer)value == 1 ? "是" : "否"); 更多單元格樣式設(shè)置請參照官方文檔 WriteCellData<?> convertToExcelData(WriteConverterContext<T> context); // return convertToExcelData(context.getValue(), context.getContentProperty(),context.getWriteContext().currentWriteHolder().globalConfiguration()); // 估計是舊版本Api,默認實現(xiàn)調(diào)用上面方法,所以只重寫上面方法就可以了
使用converter
- 上述代碼只是創(chuàng)建了一個自定義的Converter,如果要使用這個Converter需要指定使用,有兩種使用方法:
- 加在指定字段上,指定該字段使用該Converter,注意類型不要搞錯,如果該實體內(nèi)有該類型字段需要指定多次
@JsonFormat(pattern = "yyyy-MM-dd") @ExcelProperty(value = "開始時間",converter = SpecClassConverter .class) private LocalDate startTime; @JsonFormat(pattern = "yyyy-MM-dd") @ExcelProperty(value = "結(jié)束時間",converter = SpecClassConverter .class) private LocalDate endTime;
- 也可以在寫入Excel時將該Converter注冊到ExcelWriterBuilder中,該實體內(nèi)所有支持類型都會被SpecConverter轉(zhuǎn)換。
EasyExcel.write(out, SpecClass.class).registerConverter(new SpecClassConverter()); // 只是簡單的完成數(shù)據(jù)轉(zhuǎn)換,如果需要記錄日志等功能,可以將converter托管給spring成為一個bean再引入使用,converter作為默認bean時注意單例的多線程問題
- 注意:如果字段上指定了converter,ExcelWriterBuilder中也注冊了Converter且類型支持,字段上的converter優(yōu)先級最高,ExcelWriterBuilder中的converter在該字段不執(zhí)行
進階使用
在數(shù)據(jù)轉(zhuǎn)換時可以對單元格格式做操作,比如設(shè)置不同的單元格顏色、設(shè)置不同的字體、設(shè)置不同的字號
對于舊實體的處理 (例如: 數(shù)字脫敏)
實體類內(nèi)字段指定使用Converter
// 代碼很簡單,重寫converter的時候指定處理規(guī)則,然后將要脫敏的字段指定使用該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 內(nèi)的value
// 代碼思路是在類上注冊這個轉(zhuǎn)換器,因為我們有個系統(tǒng)是提數(shù)系統(tǒng),所有的導(dǎo)出最后統(tǒng)一由一個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,固定導(dǎo)出列頭,利用反射拿到注解 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>>格式,不確定字段名稱,只確定導(dǎo)出header
思路是獲取到設(shè)置的head列表,根據(jù)當前列的索引值獲取當前header,判斷是否處理,在converter中無法獲取當前columnIndex,所以放到了CellWriteHandler中。
// 查看registerWriteHandler方法,發(fā)現(xiàn)這個WriteHandler 的調(diào)用更像是一個鏈路,所以不用擔心覆蓋,可以注冊多個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方法,在數(shù)據(jù)轉(zhuǎn)換處理后對單元格做操作 @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會空指針,而且這種對數(shù)據(jù)的處理可以的話盡量放到Converter更好 // if(head.getFieldName().equals("handler")){ // String stringValue = cellData.getStringValue(); // String s = stringValue.replaceAll("[0123456789]", "*"); // cellData.setStringValue(s); // } } } }
總結(jié)
到此這篇關(guān)于Java中EasyExcel使用自定義Converter處理方法的文章就介紹到這了,更多相關(guān)EasyExcel自定義Converter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Data JPA使用JPQL與原生SQL進行查詢的操作
這篇文章主要介紹了Spring Data JPA使用JPQL與原生SQL進行查詢的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06解決RedisTemplate存儲至緩存數(shù)據(jù)出現(xiàn)亂碼的情況
這篇文章主要介紹了解決RedisTemplate存儲至緩存數(shù)據(jù)出現(xiàn)亂碼的情況,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03

Spring Boot應(yīng)用事件監(jiān)聽示例詳解

解決mybatis一對多查詢resultMap只返回了一條記錄問題

java如何利用poi解析doc和docx中的數(shù)據(jù)

SpringBoot項目改為SpringCloud項目使用nacos作為注冊中心的方法