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

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

解決mybatis一對(duì)多查詢r(jià)esultMap只返回了一條記錄問題

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

SpringBoot項(xiàng)目改為SpringCloud項(xiàng)目使用nacos作為注冊(cè)中心的方法

SpringBoot使用Apache?Tika檢測(cè)敏感信息