写在前面 导出Excel是系统中经常用到的功能。实现的方案也很多,可以自己去封装Apache Poi,也可以直接使用别人已经封装好的类库。如果需求简单的话,自己做实现也是可以的,所有的bug和feature都将是可控的。使用第三方的类库主要是方便,避免重复造轮子,但不好地方在于如果发现bug或者feature不满足时,会严重受限于类库版本的迭代。
在导出数据中经常会含有时间,在时间格式化时,如果不指定时区,则会使用服务器的时区进行格式化,这样可能导致导出的时间不是希望的时间。因而指定时区是一个很重要的功能。由于EasyExcel没有提供指定时区的功能,因而需要自己进行解决。
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > easyexcel</artifactId > <version > 2.1.2</version > </dependency >
实现方案 EasyExcel提供的可拓展功能为用户提供了很大的方便,特别是允许自定义转化器和监听器。这里将使用自定义转化器的功能进行解决。因为一个表中的时间一般都是在同一个时区,所以应该实现全局时区,同时应该支持动态配置,而不是硬编码一个时区到代码中。此外,这里还提供了一个设置类Date类型属性的时区的方法。
如下为最终的效果:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Getter @Setter @ToString @EqualsAndHashCode @NoArgsConstructor public static class TheDate { @DateTimeFormat ("yyyy-MM-dd hh:mm:ss:SSS" ) @ExcelProperty (index = 0 ) private Date date; @DateTimeFormat ("yyyy-MM-dd hh:mm:ss:SSS" ) @DateTimeZone ("Asia/Tokyo" ) @ExcelProperty (index = 1 ) private Date jpDate; }
这里推荐使用registerConverter
方法直接替代ExcelWriterBuilder
和ExcelReaderBuilder
中的默认的类型转化器。虽然也可以通过指定ExcelProperty.converter
的方法进行配置,但还是会稍显麻烦。
1 2 3 4 5 6 7 8 9 EasyExcel.write(file, TheDate.class) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .sheet("theDate" ).doWrite(listOriginal); List<TheDate> listUsCentralWriteUsCentralRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .head(TheDate.class).sheet().doReadSync();
定义 @DateTimeZone 注解 1 2 3 4 5 6 7 8 9 10 @Target (ElementType.FIELD)@Retention (RetentionPolicy.RUNTIME)@Inherited public @interface DateTimeZone { String value () default "" ; }
该注解指定一个Date属性的时区。
实现时区转化器:Date <-> String-> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import com.alibaba.excel.converters.date.DateStringConverter;import com.alibaba.excel.metadata.CellData;import com.alibaba.excel.metadata.GlobalConfiguration;import com.alibaba.excel.metadata.property.ExcelContentProperty;import java.text.ParseException;import java.util.Date;public class DateTimeZoneStringConverter extends DateStringConverter { private final String globalTimeZoneId; public DateTimeZoneStringConverter () { super (); globalTimeZoneId = null ; } public DateTimeZoneStringConverter (String timeZoneId) { super (); globalTimeZoneId = timeZoneId; } @Override public Date convertToJavaData (CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws ParseException { String timeZoneId = getTimeZoneId(contentProperty); String timeFormat = getTimeFormat(contentProperty); Date date = DateUtils.parseDate(cellData.getStringValue(), timeFormat , timeZoneId); return date; } @Override public CellData convertToExcelData (Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { String timeZoneId = getTimeZoneId(contentProperty); String timeFormat = getTimeFormat(contentProperty); String excelValue = DateUtils.format(value, timeFormat, timeZoneId); return new CellData(excelValue); } private String getTimeZoneId (ExcelContentProperty contentProperty) { if (contentProperty == null ) { return null ; } return DateTimeZoneUtil.getTimeZone(contentProperty.getField(), globalTimeZoneId); } private String getTimeFormat (ExcelContentProperty contentProperty) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null ) { return null ; } return contentProperty.getDateTimeFormatProperty().getFormat(); } }
com.alibaba.excel.converters.date.DateStringConverter
是EasyExcel定义的用于将Date
导出为String
的转化器。此外还有将Date
转化为Number
的转化器com.alibaba.excel.converters.date.DateNumberConverter
。
为了方便,DateTimeZoneStringConverter
直接继承了DateStringConverter
,并覆盖用于转化的两个方法convertToJavaData()
和 convertToExcelData()
。看起来修改了很多,实际上没有太大的改动,就增加了一个获取时区的方法和在SimpleDateFormat
中增加了TimeZone
。
这里的DateUtils
是重写的DateUtils,EasyExcel中的com.alibaba.excel.util.DateUtils
的实现没有支持TimeZone。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 import com.alibaba.excel.util.StringUtils;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.TimeZone;public class DateUtils { public static final String DATE_FORMAT_10 = "yyyy-MM-dd" ; public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss" ; public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss" ; public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss" ; public static final String DATE_FORMAT_19_FORWARD_SLASH = "yyyy/MM/dd HH:mm:ss" ; private static final String MINUS = "-" ; private DateUtils () { throw new AssertionError("DateUtils can't be instantiated." ); } public static Date parseDate (String dateString, String dateFormat, String timeZone) throws ParseException { if (StringUtils.isEmpty(dateFormat)) { dateFormat = switchDateFormat(dateString); } SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); if (!StringUtils.isEmpty(timeZone)) { sdf.setTimeZone(TimeZone.getTimeZone(timeZone)); } return sdf.parse(dateString); } public static Date parseDate (String dateString) throws ParseException { return parseDate(dateString, switchDateFormat(dateString), null ); } private static String switchDateFormat (String dateString) { int length = dateString.length(); switch (length) { case 19 : if (dateString.contains(MINUS)) { return DATE_FORMAT_19; } else { return DATE_FORMAT_19_FORWARD_SLASH; } case 17 : return DATE_FORMAT_17; case 14 : return DATE_FORMAT_14; case 10 : return DATE_FORMAT_10; default : throw new IllegalArgumentException("can not find date format for:" + dateString); } } public static String format (Date date, String timeZone) { return format(date, null , timeZone); } public static String format (Date date, String dateFormat, String timeZone) { if (date == null ) { return "" ; } if (StringUtils.isEmpty(dateFormat)) { dateFormat = DATE_FORMAT_19; } SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); if (!StringUtils.isEmpty(timeZone)) { sdf.setTimeZone(TimeZone.getTimeZone(timeZone)); } return sdf.format(date); } }
单独封装了TimeZone获取的方法。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import com.alibaba.excel.util.StringUtils;import java.lang.reflect.Field;public class DateTimeZoneUtil { public static String getTimeZone (Field field, String defaultTimeZoneId) { DateTimeZone dateTimeZone = field.getAnnotation(DateTimeZone.class); if (dateTimeZone == null ) { return defaultTimeZoneId; } String timeZoneId = dateTimeZone.value(); if (StringUtils.isEmpty(timeZoneId)) { return defaultTimeZoneId; } return timeZoneId; } }
实现时区转化器:Date <-> Number-> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import com.alibaba.excel.converters.date.DateNumberConverter;import com.alibaba.excel.metadata.CellData;import com.alibaba.excel.metadata.GlobalConfiguration;import com.alibaba.excel.metadata.property.ExcelContentProperty;import lombok.extern.slf4j.Slf4j;import org.apache.poi.ss.usermodel.DateUtil;import java.math.BigDecimal;import java.util.Calendar;import java.util.Date;import java.util.TimeZone;@Slf 4jpublic class DateTimeZoneNumberConverter extends DateNumberConverter { private final String globalTimeZoneId; public DateTimeZoneNumberConverter () { this (null ); } public DateTimeZoneNumberConverter (String timeZoneId) { super (); this .globalTimeZoneId = timeZoneId; } @Override public Date convertToJavaData (CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { TimeZone timeZone = getTimeZone(contentProperty); boolean use1904windowing = getUse1904windowing(contentProperty, globalConfiguration); return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), use1904windowing, timeZone); } @Override public CellData convertToExcelData (Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { TimeZone timeZone = getTimeZone(contentProperty); Calendar calendar = getCalendar(value, timeZone); boolean use1904windowing = getUse1904windowing(contentProperty, globalConfiguration); CellData cellData = new CellData(BigDecimal.valueOf(DateUtil.getExcelDate(calendar, use1904windowing))); return cellData; } private TimeZone getTimeZone (ExcelContentProperty contentProperty) { if (contentProperty == null ) { return null ; } String timeZoneId = DateTimeZoneUtil.getTimeZone(contentProperty.getField(), globalTimeZoneId); return TimeZone.getTimeZone(timeZoneId); } private Calendar getCalendar (Date date, TimeZone timeZone) { Calendar calStart = Calendar.getInstance(); calStart.setTime(date); if (timeZone != null ) { calStart.setTimeZone(timeZone); } return calStart; } private boolean getUse1904windowing (ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null ) { return contentProperty.getDateTimeFormatProperty().getUse1904windowing(); } else { return globalConfiguration.getUse1904windowing(); } } }
类似DateTimeZoneStringConverter
,DateTimeZoneNumberConverter
继承了DateNumberConverter
,提供了一个Date
与Numbe
之间转化的转化器。
测试 如下的单元测试,对@DateTimeZone
,DateTimeZoneStringConverter
和DateTimeZoneNumberConverter
均进行了测试。同时这也是一个完整的使用案例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 package io.gitlab.donespeak.tutorial.excel.easyexcel.timezone;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.annotation.ExcelProperty;import com.alibaba.excel.annotation.format.DateTimeFormat;import lombok.EqualsAndHashCode;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;import lombok.ToString;import org.junit.Rule;import org.junit.Test;import org.junit.rules.TemporaryFolder;import java.io.File;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.TimeZone;import java.util.function.Function;import java.util.stream.Collectors;import static org.junit.Assert.assertEquals;public class DateTimeZoneConverterTest { @Getter @Setter @ToString @EqualsAndHashCode @NoArgsConstructor public static class TheDate { @DateTimeFormat ("yyyy-MM-dd hh:mm:ss:SSS" ) @ExcelProperty (index = 0 ) private Date date; @DateTimeFormat ("yyyy-MM-dd hh:mm:ss:SSS" ) @DateTimeZone ("Asia/Tokyo" ) @ExcelProperty (index = 1 ) private Date jpDate; } @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private static final String TIME_ZONE_ID_US_CENTRAL = "US/Central" ; private static final String TIME_ZONE_ID_ETC_UTC = "Etc/UTC" ; private static final String TIME_ZONE_ID_JP = "Asia/Tokyo" ; public File getTestDirectory () { return temporaryFolder.getRoot(); } @Test public void testDateTimeZoneStringConverter () { File file = new File(getTestDirectory(), "easyexcel-test-dateTimeZoneStringConverter.xlsx" ); if (file.exists()) { file.delete(); } List<TheDate> listOriginal = data(); EasyExcel.write(file, TheDate.class) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .sheet("theDate" ).doWrite(listOriginal); List<TheDate> listUsCentralWriteUsCentralRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .head(TheDate.class).sheet().doReadSync(); assertListEquals(listOriginal, listUsCentralWriteUsCentralRead); List<TheDate> listUsCentralWriteEtcUtcRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_ETC_UTC)) .head(TheDate.class).sheet().doReadSync(); System.out.println(listUsCentralWriteEtcUtcRead); assertTimeSpan(collectDate(listOriginal, d -> d.getDate()), collectDate(listUsCentralWriteEtcUtcRead, d -> d.getDate()), TIME_ZONE_ID_US_CENTRAL, TIME_ZONE_ID_ETC_UTC); assertTimeSpan(collectDate(listOriginal, d -> d.getJpDate()), collectDate(listUsCentralWriteEtcUtcRead, d -> d.getJpDate()), TIME_ZONE_ID_JP, TIME_ZONE_ID_JP); } @Test public void testDateTimeZoneNumberConverter () { File file = new File(getTestDirectory(), "easyexcel-test-dateTimeZoneNumberConverter.xlsx" ); if (file.exists()) { file.delete(); } List<TheDate> listOriginal = data(); EasyExcel.write(file, TheDate.class) .registerConverter(new DateTimeZoneNumberConverter(TIME_ZONE_ID_US_CENTRAL)) .sheet("theDate" ).doWrite(listOriginal); List<TheDate> listUsCentralWriteUsCentralRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneNumberConverter(TIME_ZONE_ID_US_CENTRAL)) .head(TheDate.class).sheet().doReadSync(); assertListEquals(listOriginal, listUsCentralWriteUsCentralRead); List<TheDate> listUsCentralWriteEtcUtcRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneNumberConverter(TIME_ZONE_ID_ETC_UTC)) .head(TheDate.class).sheet().doReadSync(); assertTimeSpan(collectDate(listOriginal, d -> d.getDate()), collectDate(listUsCentralWriteEtcUtcRead, d -> d.getDate()), TIME_ZONE_ID_US_CENTRAL, TIME_ZONE_ID_ETC_UTC); assertTimeSpan(collectDate(listOriginal, d -> d.getJpDate()), collectDate(listUsCentralWriteEtcUtcRead, d -> d.getJpDate()), TIME_ZONE_ID_JP, TIME_ZONE_ID_JP); } private List<TheDate> data () { Date now = getTime(); List<TheDate> datas = new ArrayList<>(); TheDate thd = new TheDate(); thd.setDate(now); thd.setJpDate(now); datas.add(thd); return datas; } private Date getTime () { return new Date(); } private long getTimeSpan (Date from, Date to) { return from.getTime() - to.getTime(); } private long getTimeZoneTimeSpan (String timeZoneIdfrom, String timeZoneIdTo) { return TimeZone.getTimeZone(timeZoneIdfrom).getRawOffset() - TimeZone.getTimeZone(timeZoneIdTo).getRawOffset(); } private void assertListEquals (List<TheDate> listOriginal, List<TheDate> listUsCentral) { assertEquals(listOriginal.size(), listUsCentral.size()); for (int i = 0 ; i < listOriginal.size(); i ++) { TheDate original = listOriginal.get(i); TheDate usCentral = listUsCentral.get(i); assertEquals(original, usCentral); } } private void assertTimeSpan (List<Date> dateOriginal, List<Date> dateOperated, String timeZoneWrite, String timeZoneRead) { long timeZoneSpanFromUsCentralToEtcUtc = getTimeZoneTimeSpan(timeZoneWrite, timeZoneRead); for (int i = 0 ; i < dateOriginal.size(); i ++) { long span = getTimeSpan(dateOperated.get(i), dateOriginal.get(i)); assertEquals(timeZoneSpanFromUsCentralToEtcUtc, span); } } private List<Date> collectDate ( final List<TheDate> list, Function<TheDate, Date> function) { return list.stream().map(function).collect(Collectors.toList()); } }
拓展 - 聊聊EasyExcel的转化器 写过程 1 2 3 EasyExcel.write(file, TheDate.class) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .sheet("theDate" ).doWrite(listOriginal);
EasyExcel.write(file, TheDate.class)
: 会创建一个 ExcelWriterBuilder
,目前也仅仅是设置了文件输出路径和表头格式。
registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL))
: 为 ExcelWriterBuilder.writeWorkbook
添加自定义转化器。
sheet("theDate")
: 创建ExcelWriterSheetBuilder
,并配置ExcelWriter
的上下文,也就是转化器等信息。
.doWrite(listOriginal)
: ExcelWriter
将列表生成excel文件。
转化器的配置就发生在sheet("theDate")
中。按照:1 2 3 4 ExcelWriterSheetBuilder.sheet() -> ExcelWriterSheetBuilder.build() -> new ExcelWriter(writeWorkbook) -> new ExcelBuilderImpl(writeWorkbook) -> new WriteContextImpl(writeWorkbook) -> WirteContextImpl.initCurrentSheetHolder(writeSheet) -> new WriteSheetHolder(writeSheet, writeWorkbookHolder) -> new AbstractWriteHolder()
到这里就可以找到配置Converter的代码了:1 2 3 4 5 6 7 8 9 10 11 12 13 if (parentAbstractWriteHolder == null ) { setConverterMap(DefaultConverterLoader.loadDefaultWriteConverter()); } else { setConverterMap(new HashMap<String, Converter>(parentAbstractWriteHolder.getConverterMap())); } if (writeBasicParameter.getCustomConverterList() != null && !writeBasicParameter.getCustomConverterList().isEmpty()) { for (Converter converter : writeBasicParameter.getCustomConverterList()) { getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); } }
com.alibaba.excel.converters
包下有EasyExcel提供的默认的Converter。在配置默认Converter的流程中,DefaultConverterLoader.loadDefaultWriteConverter()
将默认的转化器进行加载。返回一个以converter.supportJavaTypeKey()
构成的key,converter
作为value的Map,加载完成之后会有如下的列表(映射关系中会将基本类型转化为封装类型):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 BigDecimal.class: BigDecimalNumberConverter Boolean.class: BooleanBooleanConverter Byte.class: ByteNumberConverter Date.class: DateStringConverter Double.class: DoubleNumberConverter Float.class: FloatNumberConverter Integer.class: IntegerNumberConverter Long.class: LongNumberConverter Short.class: ShortNumberConverter String.class: StringStringConverter File.class: FileImageConverter InpurtStream.class: InputStreamImageConverter byte[].class: ByteArrayImageConverter Byte[].class: BoxingByteArrayImageConverter URL.class: UrlImageConverter
如果有自定义的Converter,则会使用自动定义的Conveter,则会根据supportJavaTypeKey
替换原来的默认的Converter。
在写入的时候,由AbstractExcelWriteExecutor
根据数据的类型,获取正确的转化器将JavaObject转化为正确的CellData
。
读过程 1 2 3 List<TheDate> listUsCentralWriteUsCentralRead = EasyExcel.read(file) .registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL)) .head(TheDate.class).sheet().doReadSync();
EasyExcel.read(file)
: 创建ExcelReaderBuilder
对象,配置输入文件位置,默认表头和默认监听器。
registerConverter(new DateTimeZoneStringConverter(TIME_ZONE_ID_US_CENTRAL))
: 为ExcelReaderBuilder.readWorkbook
添加自定义转化器。
head(TheDate.class)
: 设置表头。
sheet()
: 创建ExcelReaderSheetBuilder
,并配置ExcelReader
的上下文,也就是转化器等信息。
doReadSync()
: 同步读,将数据从文件中读取到对象列表中。
和写过程类似,转化器的配置就发生在sheet()
中,且过程基本是一样的。按照:1 2 3 4 ExcelReaderSheetBuilder.sheet() -> ExcelReaderSheetBuilder.build() -> new ExcelReader(readWorkbook) -> new ExcelAnalyserImpl(readWorkbook) -> new AnalysisContextImpl(readWorkbook) -> new ReadWorkbookHolder(readWorkbook) -> new AbstractReadHolder()
到这里就可以找到配置Converter的代码了:1 2 3 4 5 6 7 8 9 10 11 12 13 if (parentAbstractReadHolder == null ) { setConverterMap(DefaultConverterLoader.loadDefaultReadConverter()); } else { setConverterMap(new HashMap<String, Converter>(parentAbstractReadHolder.getConverterMap())); } if (readBasicParameter.getCustomConverterList() != null && !readBasicParameter.getCustomConverterList().isEmpty()) { for (Converter converter : readBasicParameter.getCustomConverterList()) { getConverterMap().put( ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), converter); } }
和写过程不同,读过程通过DefaultConverterLoader.loadDefaultReadConverter()
加载映射关系,加载之后可以得到由converter.supportJavaTypeKey()
和converter.supportExcelTypeKey()
构成的key,以converter
为value的map,有如下的映射列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 BigDecimal.class <- CellDataTypeEnum.BOOLEAN: BigDecimalBooleanConverter BigDecimal.class <- CellDataTypeEnum.NUMBER: BigDecimalNumberConverter BigDecimal.class <- CellDataTypeEnum.STRING: BigDecimalStringConverter Boolean.class <- CellDataTypeEnum.BOOLEAN: BooleanBooleanConverter Boolean.class <- CellDataTypeEnum.NUMBER: BooleanNumberConverter Boolean.class <- CellDataTypeEnum.STRING: BooleanStringConverter Byte.class <- CellDataTypeEnum.BOOLEAN: ByteBooleanConverter Byte.class <- CellDataTypeEnum.NUMBER: ByteNumberConverter Byte.class <- CellDataTypeEnum.STRING: ByteStringConverter Date.class <- CellDataTypeEnum.NUMBER: DateNumberConverter Date.class <- CellDataTypeEnum.STRING: DateStringConverter Double.class <- CellDataTypeEnum.BOOLEAN: DoubleBooleanConverter Double.class <- CellDataTypeEnum.NUMBER: DoubleNumberConverter Double.class <- CellDataTypeEnum.STRING: DoubleStringConverter Float.class <- CellDataTypeEnum.BOOLEAN: FloatBooleanConverter Float.class <- CellDataTypeEnum.NUMBER: FloatNumberConverter Float.class <- CellDataTypeEnum.STRING: FloatStringConverter Integer.class <- CellDataTypeEnum.BOOLEAN: IntegerBooleanConverter Integer.class <- CellDataTypeEnum.NUMBER: IntegerNumberConverter Integer.class <- CellDataTypeEnum.STRING: IntegerStringConverter Long.class <- CellDataTypeEnum.BOOLEAN: LongBooleanConverter Long.class <- CellDataTypeEnum.NUMBER: LongNumberConverter Long.class <- CellDataTypeEnum.STRING: LongStringConverter Long.class <- CellDataTypeEnum.BOOLEAN: LongBooleanConverter Long.class <- CellDataTypeEnum.NUMBER: LongNumberConverter Long.class <- CellDataTypeEnum.STRING: LongStringConverter Short.class <- CellDataTypeEnum.BOOLEAN: ShortBooleanConverter Short.class <- CellDataTypeEnum.NUMBER: ShortNumberConverter Short.class <- CellDataTypeEnum.STRING: ShortStringConverter String.class <- CellDataTypeEnum.BOOLEAN: StringBooleanConverter String.class <- CellDataTypeEnum.NUMBER: StringNumberConverter String.class <- CellDataTypeEnum.STRING: StringStringConverter String.class <- CellDataTypeEnum.ERROR: StringErrorConverter
和写入不同,读具有更多的组合方式,excel文件中的字段类型可以有多种,对应的javaObject的属性也可以有多种。通过这样的映射关系可以确定输入数据的类型要转化为目标数据类型所需要使用到的转化器。
如果有自定义的Converter,则会使用自动定义的Conveter,则会根据supportJavaTypeKey
和supportExcelTypeKey
替换原来的默认的Converter。
类型转化的使用就得看ReadListener
的子类的使用了。
参考和其他
源码见:tutorial/tutorial-excel 该功能已经提出issue,可以关注:希望为DateTimeFormat增加时区参数 #841
前端生成Excel的技术可以了解: