Browse Source

Merge pull request #2927 from alibaba/bugfix

Bugfix
pull/2550/merge
Jiaju Zhuang 2 years ago committed by GitHub
parent
commit
52195c957c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 5
      easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java
  3. 3
      easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java
  4. 5
      easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java
  5. 19
      easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java
  6. 9
      easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java
  7. 5
      easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java
  8. 5
      easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java
  9. 40
      easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java
  10. 3
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java
  11. 37
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java
  12. 8
      easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java
  13. 12
      easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java
  14. 63
      easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  15. 12
      easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java
  16. 17
      easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java
  17. 38
      easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java
  18. 71
      easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java
  19. 52
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java
  20. 52
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java
  21. 1
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java
  22. 35
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java
  23. 24
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java
  24. 314
      easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java
  25. BIN
      easyexcel-test/src/test/resources/compatibility/t05.xlsx
  26. BIN
      easyexcel-test/src/test/resources/compatibility/t06.xlsx
  27. BIN
      easyexcel-test/src/test/resources/compatibility/t07.xlsx
  28. 2
      pom.xml
  29. 6
      update.md

2
README.md

@ -27,7 +27,7 @@ easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
<version>3.1.5</version> <version>3.2.0</version>
</dependency> </dependency>
``` ```

5
easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java

@ -5,6 +5,7 @@ import java.util.Map;
import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler;
import com.alibaba.excel.constant.BuiltinFormats; import com.alibaba.excel.constant.BuiltinFormats;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.context.xls.XlsReadContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.enums.RowTypeEnum;
@ -56,7 +57,9 @@ public class FormulaRecordHandler extends AbstractXlsRecordHandler implements Ig
break; break;
case NUMERIC: case NUMERIC:
tempCellData.setType(CellDataTypeEnum.NUMBER); tempCellData.setType(CellDataTypeEnum.NUMBER);
tempCellData.setNumberValue(BigDecimal.valueOf(frec.getValue())); tempCellData.setOriginalNumberValue(BigDecimal.valueOf(frec.getValue()));
tempCellData.setNumberValue(
tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT));
int dataFormat = int dataFormat =
xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec); xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec);
DataFormatData dataFormatData = new DataFormatData(); DataFormatData dataFormatData = new DataFormatData();

3
easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java

@ -4,6 +4,7 @@ import java.math.BigDecimal;
import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler;
import com.alibaba.excel.constant.BuiltinFormats; import com.alibaba.excel.constant.BuiltinFormats;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.context.xls.XlsReadContext; import com.alibaba.excel.context.xls.XlsReadContext;
import com.alibaba.excel.enums.RowTypeEnum; import com.alibaba.excel.enums.RowTypeEnum;
import com.alibaba.excel.metadata.data.DataFormatData; import com.alibaba.excel.metadata.data.DataFormatData;
@ -22,7 +23,7 @@ public class NumberRecordHandler extends AbstractXlsRecordHandler implements Ign
@Override @Override
public void processRecord(XlsReadContext xlsReadContext, Record record) { public void processRecord(XlsReadContext xlsReadContext, Record record) {
NumberRecord nr = (NumberRecord)record; NumberRecord nr = (NumberRecord)record;
ReadCellData<?> cellData = ReadCellData.newInstance(BigDecimal.valueOf(nr.getValue()), nr.getRow(), ReadCellData<?> cellData = ReadCellData.newInstanceOriginal(BigDecimal.valueOf(nr.getValue()), nr.getRow(),
(int)nr.getColumn()); (int)nr.getColumn());
short dataFormat = (short)xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex( short dataFormat = (short)xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(
nr); nr);

5
easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java

@ -2,6 +2,7 @@ package com.alibaba.excel.analysis.v07.handlers;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.constant.ExcelXmlConstants; import com.alibaba.excel.constant.ExcelXmlConstants;
import com.alibaba.excel.context.xlsx.XlsxReadContext; import com.alibaba.excel.context.xlsx.XlsxReadContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
@ -88,7 +89,9 @@ public class CellTagHandler extends AbstractXlsxTagHandler {
break; break;
} }
tempCellData.setType(CellDataTypeEnum.NUMBER); tempCellData.setType(CellDataTypeEnum.NUMBER);
tempCellData.setNumberValue(BigDecimal.valueOf(Double.parseDouble(tempDataString))); tempCellData.setOriginalNumberValue(new BigDecimal(tempDataString));
tempCellData.setNumberValue(
tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT));
break; break;
default: default:
throw new IllegalStateException("Cannot set values now"); throw new IllegalStateException("Cannot set values now");

19
easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java

@ -0,0 +1,19 @@
package com.alibaba.excel.constant;
import java.math.MathContext;
import java.math.RoundingMode;
/**
* Used to store constant
*
* @author Jiaju Zhuang
*/
public class EasyExcelConstants {
/**
* Excel by default with 15 to store Numbers, and the double in Java can use to store number 17, led to the accuracy
* will be a problem. So you need to set up 15 to deal with precision
*/
public static final MathContext EXCEL_MATH_CONTEXT = new MathContext(15, RoundingMode.HALF_UP);
}

9
easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java

@ -9,6 +9,7 @@ import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.DateUtil;
@ -33,11 +34,11 @@ public class DateNumberConverter implements Converter<Date> {
public Date convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, public Date convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(),
globalConfiguration.getUse1904windowing(), null); globalConfiguration.getUse1904windowing());
} else { } else {
return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(),
contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null); contentProperty.getDateTimeFormatProperty().getUse1904windowing());
} }
} }

5
easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateNumberConverter.java

@ -9,6 +9,7 @@ import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.DateUtil;
@ -33,10 +34,10 @@ public class LocalDateNumberConverter implements Converter<LocalDateTime> {
public LocalDateTime convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, public LocalDateTime convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return DateUtil.getLocalDateTime(cellData.getNumberValue().doubleValue(), return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
globalConfiguration.getUse1904windowing()); globalConfiguration.getUse1904windowing());
} else { } else {
return DateUtil.getLocalDateTime(cellData.getNumberValue().doubleValue(), return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
contentProperty.getDateTimeFormatProperty().getUse1904windowing()); contentProperty.getDateTimeFormatProperty().getUse1904windowing());
} }
} }

5
easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java

@ -37,9 +37,8 @@ public class StringNumberConverter implements Converter<String> {
GlobalConfiguration globalConfiguration) { GlobalConfiguration globalConfiguration) {
// If there are "DateTimeFormat", read as date // If there are "DateTimeFormat", read as date
if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
return DateUtils.format( return DateUtils.format(cellData.getNumberValue(),
DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), contentProperty.getDateTimeFormatProperty().getUse1904windowing(),
contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null),
contentProperty.getDateTimeFormatProperty().getFormat()); contentProperty.getDateTimeFormatProperty().getFormat());
} }
// If there are "NumberFormat", read as number // If there are "NumberFormat", read as number

40
easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java

@ -0,0 +1,40 @@
package com.alibaba.excel.enums;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.util.StringUtils;
/**
* Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
*
* @author Jiaju Zhuang
*/
public enum ReadDefaultReturnEnum {
/**
* default.The content of cells into string, is the same as you see in the excel.
*/
STRING,
/**
* Returns the actual type.
* Will be automatically selected according to the cell contents what return type, will return the following class:
* <ol>
* <li>{@link BigDecimal}<li/>
* <li>{@link Boolean}<li/>
* <li>{@link String}<li/>
* <li>{@link LocalDateTime}<li/>
* <ol/>
*/
ACTUAL_DATA,
/**
* Return to {@link com.alibaba.excel.metadata.data.ReadCellData}, can decide which field you need.
*/
READ_CELL_DATA,
;
}

3
easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java

@ -1,6 +1,7 @@
package com.alibaba.excel.metadata.data; package com.alibaba.excel.metadata.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.AbstractCell; import com.alibaba.excel.metadata.AbstractCell;
@ -57,6 +58,7 @@ public class CellData<T> extends AbstractCell {
} }
switch (type) { switch (type) {
case STRING: case STRING:
case DIRECT_STRING:
case ERROR: case ERROR:
if (StringUtils.isEmpty(stringValue)) { if (StringUtils.isEmpty(stringValue)) {
type = CellDataTypeEnum.EMPTY; type = CellDataTypeEnum.EMPTY;
@ -76,5 +78,4 @@ public class CellData<T> extends AbstractCell {
} }
} }
} }

37
easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java

@ -1,7 +1,9 @@
package com.alibaba.excel.metadata.data; package com.alibaba.excel.metadata.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -11,6 +13,7 @@ import lombok.Setter;
/** /**
* read cell data * read cell data
* <p>
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@ -19,6 +22,29 @@ import lombok.Setter;
@EqualsAndHashCode @EqualsAndHashCode
@NoArgsConstructor @NoArgsConstructor
public class ReadCellData<T> extends CellData<T> { public class ReadCellData<T> extends CellData<T> {
/**
* originalNumberValue vs numberValue
* <ol>
* <li>
* NUMBER
* originalNumberValue: Original data and the accuracy of his is 17, but in fact the excel only 15 precision to
* process the data
* numberValue: After correction of the data and the accuracy of his is 15
* for example, originalNumberValue = `2087.0249999999996` , numberValue = `2087.03`
* </li>
* <li>
* DATE
* originalNumberValue: Storage is a data type double, accurate to milliseconds
* dateValue: Based on double converted to a date format, he will revised date difference, accurate to seconds
* for example, originalNumberValue = `44729.99998836806` ,time is:`2022-06-17 23:59:58.995`,
* But in excel is displayed:` 2022-06-17 23:59:59`, dateValue = `2022-06-17 23:59:59`
* </li>
* <ol/>
* {@link CellDataTypeEnum#NUMBER} {@link CellDataTypeEnum#DATE}
*/
private BigDecimal originalNumberValue;
/** /**
* data format. * data format.
*/ */
@ -107,11 +133,21 @@ public class ReadCellData<T> extends CellData<T> {
return cellData; return cellData;
} }
public static ReadCellData<?> newInstanceOriginal(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) {
ReadCellData<?> cellData = new ReadCellData<>(numberValue);
cellData.setRowIndex(rowIndex);
cellData.setColumnIndex(columnIndex);
cellData.setOriginalNumberValue(numberValue);
cellData.setNumberValue(numberValue.round(EasyExcelConstants.EXCEL_MATH_CONTEXT));
return cellData;
}
@Override @Override
public ReadCellData<Object> clone() { public ReadCellData<Object> clone() {
ReadCellData<Object> readCellData = new ReadCellData<>(); ReadCellData<Object> readCellData = new ReadCellData<>();
readCellData.setType(getType()); readCellData.setType(getType());
readCellData.setNumberValue(getNumberValue()); readCellData.setNumberValue(getNumberValue());
readCellData.setOriginalNumberValue(getOriginalNumberValue());
readCellData.setStringValue(getStringValue()); readCellData.setStringValue(getStringValue());
readCellData.setBooleanValue(getBooleanValue()); readCellData.setBooleanValue(getBooleanValue());
readCellData.setData(getData()); readCellData.setData(getData());
@ -123,5 +159,4 @@ public class ReadCellData<T> extends CellData<T> {
} }
return readCellData; return readCellData;
} }
} }

8
easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java

@ -212,10 +212,10 @@ public class DataFormatter {
CellFormat cfmt = CellFormat.getInstance(locale, formatStr); CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
// CellFormat requires callers to identify date vs not, so do so // CellFormat requires callers to identify date vs not, so do so
Object cellValueO = data; Object cellValueO = data;
if (DateUtil.isADateFormat(dataFormat, formatStr) && if (DateUtils.isADateFormat(dataFormat, formatStr) &&
// don't try to handle Date value 0, let a 3 or 4-part format take care of it // don't try to handle Date value 0, let a 3 or 4-part format take care of it
data.doubleValue() != 0.0) { data.doubleValue() != 0.0) {
cellValueO = DateUtil.getJavaDate(data, use1904windowing); cellValueO = DateUtils.getJavaDate(data, use1904windowing);
} }
// Wrap and return (non-cachable - CellFormat does that) // Wrap and return (non-cachable - CellFormat does that)
return new CellFormatResultWrapper(cfmt.apply(cellValueO)); return new CellFormatResultWrapper(cfmt.apply(cellValueO));
@ -243,6 +243,8 @@ public class DataFormatter {
return format; return format;
} }
private Format createFormat(Short dataFormat, String dataFormatString) { private Format createFormat(Short dataFormat, String dataFormatString) {
String formatStr = dataFormatString; String formatStr = dataFormatString;
@ -628,7 +630,7 @@ public class DataFormatter {
// Hint about the raw excel value // Hint about the raw excel value
((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data); ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data);
} }
return performDateFormatting(DateUtil.getJavaDate(data, use1904windowing), dateFormat); return performDateFormatting(DateUtils.getJavaDate(data, use1904windowing), dateFormat);
} }
/** /**

12
easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java

@ -13,6 +13,7 @@ import com.alibaba.excel.cache.ReadCache;
import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.cache.selector.ReadCacheSelector;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.event.SyncReadListener; import com.alibaba.excel.event.SyncReadListener;
import com.alibaba.excel.read.listener.ModelBuildEventListener; import com.alibaba.excel.read.listener.ModelBuildEventListener;
@ -198,6 +199,17 @@ public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder<Exce
return this; return this;
} }
/**
* Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
* Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
*
* @see ReadDefaultReturnEnum
*/
public ExcelReaderBuilder readDefaultReturn(ReadDefaultReturnEnum readDefaultReturn) {
readWorkbook.setReadDefaultReturn(readDefaultReturn);
return this;
}
public ExcelReader build() { public ExcelReader build() {
return new ExcelReader(readWorkbook); return new ExcelReader(readWorkbook);
} }

63
easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java

@ -1,21 +1,30 @@
package com.alibaba.excel.read.listener; package com.alibaba.excel.read.listener;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map; import java.util.Map;
import javax.print.DocFlavor.STRING;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.alibaba.excel.util.BeanMapUtils; import com.alibaba.excel.util.BeanMapUtils;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.util.ClassUtils; import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.ConverterUtils; import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.util.MapUtils; import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.util.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cglib.beans.BeanMap; import org.springframework.cglib.beans.BeanMap;
/** /**
@ -33,13 +42,13 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<
.setCurrentRowAnalysisResult(buildUserModel(cellDataMap, readSheetHolder, context)); .setCurrentRowAnalysisResult(buildUserModel(cellDataMap, readSheetHolder, context));
return; return;
} }
context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, readSheetHolder, context)); context.readRowHolder().setCurrentRowAnalysisResult(buildNoModel(cellDataMap, readSheetHolder, context));
} }
private Object buildStringList(Map<Integer, ReadCellData<?>> cellDataMap, ReadSheetHolder readSheetHolder, private Object buildNoModel(Map<Integer, ReadCellData<?>> cellDataMap, ReadSheetHolder readSheetHolder,
AnalysisContext context) { AnalysisContext context) {
int index = 0; int index = 0;
Map<Integer, String> map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size()); Map<Integer, Object> map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size());
for (Map.Entry<Integer, ReadCellData<?>> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, ReadCellData<?>> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey(); Integer key = entry.getKey();
ReadCellData<?> cellData = entry.getValue(); ReadCellData<?> cellData = entry.getValue();
@ -48,9 +57,23 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<
index++; index++;
} }
index++; index++;
ReadDefaultReturnEnum readDefaultReturn = context.readWorkbookHolder().getReadDefaultReturn();
if (readDefaultReturn == ReadDefaultReturnEnum.STRING) {
// string
map.put(key, map.put(key,
(String)ConverterUtils.convertToJavaObject(cellData, null, null, readSheetHolder.converterMap(), (String)ConverterUtils.convertToJavaObject(cellData, null, null, readSheetHolder.converterMap(),
context, context.readRowHolder().getRowIndex(), key)); context, context.readRowHolder().getRowIndex(), key));
} else {
// retrun ReadCellData
ReadCellData<?> convertedReadCellData = convertReadCellData(cellData,
context.readWorkbookHolder().getReadDefaultReturn(), readSheetHolder, context, key);
if (readDefaultReturn == ReadDefaultReturnEnum.READ_CELL_DATA) {
map.put(key, convertedReadCellData);
} else {
map.put(key, convertedReadCellData.getData());
}
}
} }
// fix https://github.com/alibaba/easyexcel/issues/2014 // fix https://github.com/alibaba/easyexcel/issues/2014
int headSize = calculateHeadSize(readSheetHolder); int headSize = calculateHeadSize(readSheetHolder);
@ -61,6 +84,38 @@ public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<
return map; return map;
} }
private ReadCellData convertReadCellData(ReadCellData<?> cellData, ReadDefaultReturnEnum readDefaultReturn,
ReadSheetHolder readSheetHolder, AnalysisContext context, Integer columnIndex) {
Class<?> classGeneric;
switch (cellData.getType()) {
case STRING:
case DIRECT_STRING:
case ERROR:
case EMPTY:
classGeneric = String.class;
break;
case BOOLEAN:
classGeneric = Boolean.class;
break;
case NUMBER:
DataFormatData dataFormatData = cellData.getDataFormatData();
if (dataFormatData != null && DateUtils.isADateFormat(dataFormatData.getIndex(),
dataFormatData.getFormat())) {
classGeneric = LocalDateTime.class;
} else {
classGeneric = BigDecimal.class;
}
break;
default:
classGeneric = ConverterUtils.defaultClassGeneric;
break;
}
return (ReadCellData)ConverterUtils.convertToJavaObject(cellData, null, ReadCellData.class,
classGeneric, null, readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(),
columnIndex);
}
private int calculateHeadSize(ReadSheetHolder readSheetHolder) { private int calculateHeadSize(ReadSheetHolder readSheetHolder) {
if (readSheetHolder.excelReadHeadProperty().getHeadMap().size() > 0) { if (readSheetHolder.excelReadHeadProperty().getHeadMap().size() > 0) {
return readSheetHolder.excelReadHeadProperty().getHeadMap().size(); return readSheetHolder.excelReadHeadProperty().getHeadMap().size();

12
easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java

@ -11,6 +11,7 @@ import com.alibaba.excel.cache.ReadCache;
import com.alibaba.excel.cache.selector.ReadCacheSelector; import com.alibaba.excel.cache.selector.ReadCacheSelector;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.ModelBuildEventListener; import com.alibaba.excel.read.listener.ModelBuildEventListener;
import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.support.ExcelTypeEnum;
@ -62,7 +63,6 @@ public class ReadWorkbook extends ReadBasicParameter {
/** /**
* This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)} * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)}
* {@link AnalysisContext#getCustom()} * {@link AnalysisContext#getCustom()}
*
*/ */
private Object customObject; private Object customObject;
/** /**
@ -96,8 +96,18 @@ public class ReadWorkbook extends ReadBasicParameter {
* Whether to use the default listener, which is used by default. * Whether to use the default listener, which is used by default.
* <p> * <p>
* The {@link ModelBuildEventListener} is loaded by default to convert the object. * The {@link ModelBuildEventListener} is loaded by default to convert the object.
* defualt is true.
*/ */
private Boolean useDefaultListener; private Boolean useDefaultListener;
/**
* Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
* Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
*
* @see ReadDefaultReturnEnum
*/
private ReadDefaultReturnEnum readDefaultReturn;
/** /**
* Read some additional fields. None are read by default. * Read some additional fields. None are read by default.
* *

17
easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java

@ -14,6 +14,7 @@ import com.alibaba.excel.cache.selector.SimpleReadCacheSelector;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum; import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.enums.HolderEnum;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadSheet;
@ -64,10 +65,20 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
* if false, Will transfer 'inputStream' to temporary files to improve efficiency * if false, Will transfer 'inputStream' to temporary files to improve efficiency
*/ */
private Boolean mandatoryUseInputStream; private Boolean mandatoryUseInputStream;
/** /**
* Default true * Default true
*/ */
private Boolean autoCloseStream; private Boolean autoCloseStream;
/**
* Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type.
* Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`.
*
* @see ReadDefaultReturnEnum
*/
private ReadDefaultReturnEnum readDefaultReturn;
/** /**
* Excel type * Excel type
*/ */
@ -146,6 +157,12 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
this.autoCloseStream = readWorkbook.getAutoCloseStream(); this.autoCloseStream = readWorkbook.getAutoCloseStream();
} }
if (readWorkbook.getReadDefaultReturn() == null) {
this.readDefaultReturn = ReadDefaultReturnEnum.STRING;
} else {
this.readDefaultReturn = readWorkbook.getReadDefaultReturn();
}
this.customObject = readWorkbook.getCustomObject(); this.customObject = readWorkbook.getCustomObject();
if (readWorkbook.getIgnoreEmptyRow() == null) { if (readWorkbook.getIgnoreEmptyRow() == null) {
this.ignoreEmptyRow = Boolean.TRUE; this.ignoreEmptyRow = Boolean.TRUE;

38
easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java

@ -84,24 +84,52 @@ public class ConverterUtils {
public static Object convertToJavaObject(ReadCellData<?> cellData, Field field, public static Object convertToJavaObject(ReadCellData<?> cellData, Field field,
ExcelContentProperty contentProperty, Map<ConverterKey, Converter<?>> converterMap, AnalysisContext context, ExcelContentProperty contentProperty, Map<ConverterKey, Converter<?>> converterMap, AnalysisContext context,
Integer rowIndex, Integer columnIndex) { Integer rowIndex, Integer columnIndex) {
Class<?> clazz; return convertToJavaObject(cellData, field, null, null, contentProperty, converterMap, context, rowIndex,
columnIndex);
}
/**
* Convert it into a Java object
*
* @param cellData
* @param field
* @param clazz
* @param contentProperty
* @param converterMap
* @param context
* @param rowIndex
* @param columnIndex
* @return
*/
public static Object convertToJavaObject(ReadCellData<?> cellData, Field field, Class<?> clazz,
Class<?> classGeneric, ExcelContentProperty contentProperty, Map<ConverterKey, Converter<?>> converterMap,
AnalysisContext context, Integer rowIndex, Integer columnIndex) {
if (clazz == null) {
if (field == null) { if (field == null) {
clazz = String.class; clazz = String.class;
} else { } else {
clazz = field.getType(); clazz = field.getType();
} }
}
if (clazz == CellData.class || clazz == ReadCellData.class) { if (clazz == CellData.class || clazz == ReadCellData.class) {
Class<?> classGeneric = getClassGeneric(field.getGenericType());
ReadCellData<Object> cellDataReturn = cellData.clone(); ReadCellData<Object> cellDataReturn = cellData.clone();
cellDataReturn.setData(doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, cellDataReturn.setData(
context, rowIndex, columnIndex)); doConvertToJavaObject(cellData, getClassGeneric(field, classGeneric), contentProperty,
converterMap, context, rowIndex, columnIndex));
return cellDataReturn; return cellDataReturn;
} }
return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, context, rowIndex, return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, context, rowIndex,
columnIndex); columnIndex);
} }
private static Class<?> getClassGeneric(Type type) { private static Class<?> getClassGeneric(Field field, Class<?> classGeneric) {
if (classGeneric != null) {
return classGeneric;
}
if (field == null) {
return defaultClassGeneric;
}
Type type = field.getGenericType();
if (!(type instanceof ParameterizedType)) { if (!(type instanceof ParameterizedType)) {
return defaultClassGeneric; return defaultClassGeneric;
} }

71
easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java

@ -1,9 +1,11 @@
package com.alibaba.excel.util; package com.alibaba.excel.util;
import java.math.BigDecimal;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -11,6 +13,8 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.poi.ss.usermodel.DateUtil;
/** /**
* Date utils * Date utils
* *
@ -184,6 +188,33 @@ public class DateUtils {
} }
} }
/**
* Format date
*
* @param date
* @param dateFormat
* @return
*/
public static String format(LocalDateTime date, String dateFormat) {
return format(date, dateFormat, null);
}
/**
* Format date
*
* @param date
* @param dateFormat
* @return
*/
public static String format(BigDecimal date, Boolean use1904windowing, String dateFormat) {
if (date == null) {
return null;
}
LocalDateTime localDateTime = DateUtil.getLocalDateTime(date.doubleValue(),
BooleanUtils.isTrue(use1904windowing), true);
return format(localDateTime, dateFormat);
}
private static DateFormat getCacheDateFormat(String dateFormat) { private static DateFormat getCacheDateFormat(String dateFormat) {
Map<String, SimpleDateFormat> dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get(); Map<String, SimpleDateFormat> dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get();
if (dateFormatMap == null) { if (dateFormatMap == null) {
@ -200,6 +231,46 @@ public class DateUtils {
return simpleDateFormat; return simpleDateFormat;
} }
/**
* Given an Excel date with either 1900 or 1904 date windowing,
* converts it to a java.util.Date.
*
* Excel Dates and Times are stored without any timezone
* information. If you know (through other means) that your file
* uses a different TimeZone to the system default, you can use
* this version of the getJavaDate() method to handle it.
*
* @param date The Excel date.
* @param use1904windowing true if date uses 1904 windowing,
* or false if using 1900 date windowing.
* @return Java representation of the date, or null if date is not a valid Excel date
*/
public static Date getJavaDate(double date, boolean use1904windowing) {
//To calculate the Date, in the use of `org.apache.poi.ss.usermodel.DateUtil.getJavaDate(double, boolean,
// java.util.TimeZone, boolean), Date when similar `2023-01-01 00:00:00.500`, returns the`2023-01-01
// 00:00:01`, but excel in fact shows the `2023-01-01 00:00:00`.
// `org.apache.poi.ss.usermodel.DateUtil.getLocalDateTime(double, boolean, boolean)` There is no problem.
return Date.from(getLocalDateTime(date, use1904windowing).atZone(ZoneId.systemDefault()).toInstant());
}
/**
* Given an Excel date with either 1900 or 1904 date windowing,
* converts it to a java.time.LocalDateTime.
*
* Excel Dates and Times are stored without any timezone
* information. If you know (through other means) that your file
* uses a different TimeZone to the system default, you can use
* this version of the getJavaDate() method to handle it.
*
* @param date The Excel date.
* @param use1904windowing true if date uses 1904 windowing,
* or false if using 1900 date windowing.
* @return Java representation of the date, or null if date is not a valid Excel date
*/
public static LocalDateTime getLocalDateTime(double date, boolean use1904windowing) {
return DateUtil.getLocalDateTime(date, use1904windowing, true);
}
/** /**
* Determine if it is a date format. * Determine if it is a date format.
* *

52
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java

@ -1,10 +1,13 @@
package com.alibaba.easyexcel.test.core.compatibility; package com.alibaba.easyexcel.test.core.compatibility;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.constant.EasyExcelConstants;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -64,4 +67,53 @@ public class CompatibilityTest {
Map<Integer, Object> row0 = list.get(0); Map<Integer, Object> row0 = list.get(0);
Assert.assertEquals("QQSJK28F152A012242S0081", row0.get(5)); Assert.assertEquals("QQSJK28F152A012242S0081", row0.get(5));
} }
@Test
public void t05() {
// https://github.com/alibaba/easyexcel/issues/1956
// Excel read date needs to be rounded
List<Map<Integer, String>> list = EasyExcel
.read(TestFileUtil.getPath() + "compatibility/t05.xlsx")
.sheet()
.doReadSync();
log.info("data:{}", JSON.toJSONString(list));
Assert.assertEquals("2023-01-01 00:00:00", list.get(0).get(0));
Assert.assertEquals("2023-01-01 00:00:00", list.get(1).get(0));
Assert.assertEquals("2023-01-01 00:00:00", list.get(2).get(0));
Assert.assertEquals("2023-01-01 00:00:01", list.get(3).get(0));
Assert.assertEquals("2023-01-01 00:00:01", list.get(4).get(0));
}
@Test
public void t06() {
// Keep error precision digital format
List<Map<Integer, String>> list = EasyExcel
.read(TestFileUtil.getPath() + "compatibility/t06.xlsx")
.headRowNumber(0)
.sheet()
.doReadSync();
log.info("data:{}", JSON.toJSONString(list));
Assert.assertEquals("2087.03", list.get(0).get(2));
}
@Test
public void t07() {
// https://github.com/alibaba/easyexcel/issues/2805
// Excel read date needs to be rounded
List<Map<Integer, Object>> list = EasyExcel
.read(TestFileUtil.getPath() + "compatibility/t07.xlsx")
.readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA)
.sheet()
.doReadSync();
log.info("data:{}", JSON.toJSONString(list));
Assert.assertEquals(0, new BigDecimal("24.1998124").compareTo((BigDecimal)list.get(0).get(11)));
list = EasyExcel
.read(TestFileUtil.getPath() + "compatibility/t07.xlsx")
.sheet()
.doReadSync();
log.info("data:{}", JSON.toJSONString(list));
Assert.assertEquals("24.20", list.get(0).get(11));
}
} }

52
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java

@ -1,14 +1,20 @@
package com.alibaba.easyexcel.test.core.nomodel; package com.alibaba.easyexcel.test.core.nomodel;
import java.io.File; import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.enums.ReadDefaultReturnEnum;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.util.DateUtils; import com.alibaba.excel.util.DateUtils;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
@ -19,6 +25,7 @@ import org.junit.runners.MethodSorters;
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Slf4j
public class NoModelDataTest { public class NoModelDataTest {
private static File file07; private static File file07;
@ -28,7 +35,6 @@ public class NoModelDataTest {
private static File fileRepeat03; private static File fileRepeat03;
private static File fileRepeatCsv; private static File fileRepeatCsv;
@BeforeClass @BeforeClass
public static void init() { public static void init() {
file07 = TestFileUtil.createNewFile("noModel07.xlsx"); file07 = TestFileUtil.createNewFile("noModel07.xlsx");
@ -41,20 +47,20 @@ public class NoModelDataTest {
@Test @Test
public void t01ReadAndWrite07() throws Exception { public void t01ReadAndWrite07() throws Exception {
readAndWrite(file07, fileRepeat07); readAndWrite(file07, fileRepeat07, false);
} }
@Test @Test
public void t02ReadAndWrite03() throws Exception { public void t02ReadAndWrite03() throws Exception {
readAndWrite(file03, fileRepeat03); readAndWrite(file03, fileRepeat03, false);
} }
@Test @Test
public void t03ReadAndWriteCsv() throws Exception { public void t03ReadAndWriteCsv() throws Exception {
readAndWrite(fileCsv, fileRepeatCsv); readAndWrite(fileCsv, fileRepeatCsv, true);
} }
private void readAndWrite(File file, File fileRepeat) throws Exception { private void readAndWrite(File file, File fileRepeat, boolean isCsv) throws Exception {
EasyExcel.write(file).sheet().doWrite(data()); EasyExcel.write(file).sheet().doWrite(data());
List<Map<Integer, String>> result = EasyExcel.read(file).headRowNumber(0).sheet().doReadSync(); List<Map<Integer, String>> result = EasyExcel.read(file).headRowNumber(0).sheet().doReadSync();
Assert.assertEquals(10, result.size()); Assert.assertEquals(10, result.size());
@ -63,6 +69,42 @@ public class NoModelDataTest {
Assert.assertEquals("109", data10.get(1)); Assert.assertEquals("109", data10.get(1));
Assert.assertEquals("2020-01-01 01:01:01", data10.get(2)); Assert.assertEquals("2020-01-01 01:01:01", data10.get(2));
List<Map<Integer, Object>> actualDataList = EasyExcel.read(file)
.headRowNumber(0)
.readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA)
.sheet()
.doReadSync();
log.info("actualDataList:{}", JSON.toJSONString(actualDataList));
Assert.assertEquals(10, actualDataList.size());
Map<Integer, Object> actualData10 = actualDataList.get(9);
Assert.assertEquals("string19", actualData10.get(0));
if (isCsv) {
// CSV only string type
Assert.assertEquals("109", actualData10.get(1));
Assert.assertEquals("2020-01-01 01:01:01", actualData10.get(2));
} else {
Assert.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)actualData10.get(1)));
Assert.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), actualData10.get(2));
}
List<Map<Integer, ReadCellData<?>>> readCellDataList = EasyExcel.read(file)
.headRowNumber(0)
.readDefaultReturn(ReadDefaultReturnEnum.READ_CELL_DATA)
.sheet()
.doReadSync();
log.info("readCellDataList:{}", JSON.toJSONString(readCellDataList));
Assert.assertEquals(10, readCellDataList.size());
Map<Integer, ReadCellData<?>> readCellData10 = readCellDataList.get(9);
Assert.assertEquals("string19", readCellData10.get(0).getData());
if (isCsv) {
// CSV only string type
Assert.assertEquals("109", readCellData10.get(1).getData());
Assert.assertEquals("2020-01-01 01:01:01", readCellData10.get(2).getData());
} else {
Assert.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)readCellData10.get(1).getData()));
Assert.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), readCellData10.get(2).getData());
}
EasyExcel.write(fileRepeat).sheet().doWrite(result); EasyExcel.write(fileRepeat).sheet().doWrite(result);
result = EasyExcel.read(fileRepeat).headRowNumber(0).sheet().doReadSync(); result = EasyExcel.read(fileRepeat).headRowNumber(0).sheet().doReadSync();
Assert.assertEquals(10, result.size()); Assert.assertEquals(10, result.size());

1
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java

@ -24,6 +24,7 @@ public class DemoData {
private Date date; private Date date;
@ExcelProperty("数字标题") @ExcelProperty("数字标题")
private Double doubleData; private Double doubleData;
/** /**
* 忽略这个字段 * 忽略这个字段
*/ */

35
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java

@ -0,0 +1,35 @@
package com.alibaba.easyexcel.test.temp;
import java.math.BigDecimal;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
/**
* 基础数据类
*
* @author Jiaju Zhuang
**/
@Getter
@Setter
@EqualsAndHashCode
public class DemoData2 {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
@ExcelProperty("数字标题2")
private BigDecimal bigDecimal;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}

24
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java

@ -0,0 +1,24 @@
package com.alibaba.easyexcel.test.temp;
import java.time.LocalDateTime;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
/**
* 基础数据类
*
* @author Jiaju Zhuang
**/
@Getter
@Setter
@EqualsAndHashCode
public class DemoData3 {
@ExcelProperty("日期时间标题")
private LocalDateTime localDateTime;
}

314
easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java

@ -1,32 +1,49 @@
package com.alibaba.easyexcel.test.temp; package com.alibaba.easyexcel.test.temp;
import java.io.File; import java.io.File;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.demo.write.DemoData;
import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.util.NumberDataFormatterUtils;
import com.alibaba.excel.util.NumberUtils;
import com.alibaba.excel.util.PositionUtils; import com.alibaba.excel.util.PositionUtils;
import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.IndexedColors;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/** /**
* 临时测试 * 临时测试
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
**/ **/
@Slf4j
public class Lock2Test { public class Lock2Test {
private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class); private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class);
@ -37,7 +54,11 @@ public class Lock2Test {
// File file = TestFileUtil.readUserHomeFile("test/test6.xls"); // File file = TestFileUtil.readUserHomeFile("test/test6.xls");
File file = new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/src/test/resources/converter/converter07.xlsx"); File file = new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/src/test/resources/converter/converter07.xlsx");
List<Object> list = EasyExcel.read("/Users/zhuangjiaju/Downloads/测试格式.xlsx").sheet(0).headRowNumber(0).doReadSync(); List<Object> list = EasyExcel.read(
"/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/target/test-classes/simpleWrite1674051907397.xlsx")
//.useDefaultListener(false)
.sheet(0)
.headRowNumber(0).doReadSync();
LOGGER.info("数据:{}", list.size()); LOGGER.info("数据:{}", list.size());
for (Object data : list) { for (Object data : list) {
LOGGER.info("返回数据:{}", CollectionUtils.size(data)); LOGGER.info("返回数据:{}", CollectionUtils.size(data));
@ -109,18 +130,6 @@ public class Lock2Test {
return list; return list;
} }
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
@Test @Test
public void testc() throws Exception { public void testc() throws Exception {
LOGGER.info("reslut:{}", JSON.toJSONString(new CellReference("B3"))); LOGGER.info("reslut:{}", JSON.toJSONString(new CellReference("B3")));
@ -151,10 +160,9 @@ public class Lock2Test {
@Test @Test
public void test335() throws Exception { public void test335() throws Exception {
LOGGER.info("reslut:{}", PositionUtils.getCol("A10", null));
LOGGER.info("reslut:{}", PositionUtils.getCol("A10",null));
LOGGER.info("reslut:{}", PositionUtils.getRow("A10")); LOGGER.info("reslut:{}", PositionUtils.getRow("A10"));
LOGGER.info("reslut:{}", PositionUtils.getCol("AB10",null)); LOGGER.info("reslut:{}", PositionUtils.getCol("AB10", null));
LOGGER.info("reslut:{}", PositionUtils.getRow("AB10")); LOGGER.info("reslut:{}", PositionUtils.getRow("AB10"));
//LOGGER.info("reslut:{}", PositionUtils2.getCol("A10",null)); //LOGGER.info("reslut:{}", PositionUtils2.getCol("A10",null));
@ -163,5 +171,279 @@ public class Lock2Test {
//LOGGER.info("reslut:{}", PositionUtils2.getRow("AB10")); //LOGGER.info("reslut:{}", PositionUtils2.getRow("AB10"));
} }
@Test
public void numberforamt() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.99998836806), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592).setScale(10, RoundingMode
// .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592).setScale(10, RoundingMode
// .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//44729.9999883681
//44729.999988368058
//LOGGER.info("date:{}",
// NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode
// .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss",
// null,
// null, null));
//LOGGER.info("date:{}",BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode.HALF_UP).doubleValue
// ());
// 2022/6/17 23:59:59
// 期望 44729.99998842592
//LOGGER.info("data:{}", DateUtil.getJavaDate(44729.9999883681, true));
LOGGER.info("data4:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058)
.setScale(4, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data5:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058)
.setScale(5, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data6:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058)
.setScale(6, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data7:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058)
.setScale(7, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data8:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058)
.setScale(8, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false)));
LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.9999883681, false)));
LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.999988368058"), false));
LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.9999883681"), false));
// 44729.999976851854
// 44729.999988368058
LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:58")));
// 44729.99998842592
LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:59")));
LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854)
.setScale(10, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592)
.setScale(10, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854)
.setScale(5, RoundingMode.HALF_UP).doubleValue(), false));
LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592)
.setScale(5, RoundingMode.HALF_UP).doubleValue(), false));
}
@Test
public void testDate() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info("TT:{}", format.format(new Date(100L)));
log.info("TT:{}", new Date().getTime());
}
@Test
public void testDateAll() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
long dateTime = 0L;
while (true) {
Date date = new Date(dateTime);
double excelDate = DateUtil.getExcelDate(date);
Assert.assertEquals("测试基本转换错误" + dateTime, format.format(date),
format.format(DateUtil.getJavaDate(excelDate, false)));
Assert.assertEquals("测试精度5转换错误" + dateTime, format.format(date),
format.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate)
.setScale(10, RoundingMode.HALF_UP).doubleValue(), false)));
LOGGER.info("date:{}", format2.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate)
.setScale(10, RoundingMode.HALF_UP).doubleValue())));
dateTime += 1000L;
// 30天输出
if (dateTime % (24 * 60 * 60 * 1000) == 0) {
log.info("{}成功", format.format(date));
}
if (dateTime > 1673957544750L) {
log.info("结束啦");
break;
}
}
log.info("结束啦");
}
@Test
public void numberforamt3() throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
List<Map<Integer, ReadCellData>> list = EasyExcel.read("/Users/zhuangjiaju/Downloads/date3.xlsx")
.useDefaultListener(false)
.sheet(0)
.headRowNumber(0).doReadSync();
LOGGER.info("数据:{}", list.size());
for (Map<Integer, ReadCellData> readCellDataMap : list) {
ReadCellData data = readCellDataMap.get(0);
LOGGER.info("data:{}", format.format(
DateUtil.getJavaDate(data.getNumberValue().setScale(10, RoundingMode.HALF_UP).doubleValue(), false)));
}
//
//LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44727.999988425923, false)));
//LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false)));
}
@Test
public void numberforamt4() throws Exception {
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class)
.sheet("模板")
.doWrite(() -> {
// 分页查询数据
return data2();
});
}
@Test
public void numberforamt77() throws Exception {
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData3.class)
.sheet("模板")
.doWrite(() -> {
List<DemoData3> list = new ArrayList<>();
DemoData3 demoData3 = new DemoData3();
demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 400000000));
list.add(demoData3);
demoData3 = new DemoData3();
demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 499000000));
list.add(demoData3);
demoData3 = new DemoData3();
demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 500000000));
list.add(demoData3);
demoData3 = new DemoData3();
demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 501000000));
list.add(demoData3);
demoData3 = new DemoData3();
demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000));
list.add(demoData3);
return list;
});
}
@Test
public void numberforamt99() throws Exception {
LocalDateTime localDateTime=LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000);
log.info("date:{}",localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
}
@Test
public void numberforamt5() throws Exception {
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class)
.sheet("模板")
.doWrite(() -> {
// 分页查询数据
return data3();
});
}
@Test
public void numberforamt6() throws Exception {
DecimalFormat decimalFormat = new DecimalFormat("#.#");
BigDecimal bigDecimal = new BigDecimal(3101011021236149800L);
log.info("b:{}", bigDecimal);
log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP));
log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP)));
}
@Test
public void numberforamt7() throws Exception {
DecimalFormat decimalFormat = new DecimalFormat("#.#");
BigDecimal bigDecimal = new BigDecimal(3.1010110212361498E+18).round(new MathContext(15, RoundingMode.HALF_UP));
//bigDecimal.
// bigDecimal
log.info("b:{}", bigDecimal);
log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP));
log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP)));
log.info("b:{}", decimalFormat.format(bigDecimal));
}
private List<DemoData2> data3() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
List<DemoData2> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
DemoData2 data = new DemoData2();
data.setString("字符串" + i);
data.setDoubleData(0.56);
data.setBigDecimal(BigDecimal.valueOf(3101011021236149800L));
list.add(data);
}
return list;
}
private List<DemoData> data() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
try {
data.setDate(format.parse("2032-01-18 09:00:01.995"));
} catch (ParseException e) {
throw new RuntimeException(e);
}
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
private List<DemoData> data2() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
try {
data.setDate(format.parse("2032-01-18 09:00:00."));
} catch (ParseException e) {
throw new RuntimeException(e);
}
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
} }

BIN
easyexcel-test/src/test/resources/compatibility/t05.xlsx

Binary file not shown.

BIN
easyexcel-test/src/test/resources/compatibility/t06.xlsx

Binary file not shown.

BIN
easyexcel-test/src/test/resources/compatibility/t07.xlsx

Binary file not shown.

2
pom.xml

@ -20,7 +20,7 @@
<properties> <properties>
<revision>3.1.5</revision> <revision>3.2.0</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>1.8</jdk.version> <jdk.version>1.8</jdk.version>
<gpg.skip>true</gpg.skip> <gpg.skip>true</gpg.skip>

6
update.md

@ -1,3 +1,9 @@
# 3.2.0
* 修复部分xlsx读取日期可能相差1秒的bug [Issue #1956](https://github.com/alibaba/easyexcel/issues/1956)
* 修复部分数据精度和excel不匹配的bug [Issue #2805](https://github.com/alibaba/easyexcel/issues/2805)
* 不创建对象的读支持读取原始的数据类型
# 3.1.5 # 3.1.5
* 提高xlsx读取兼容性:兼用ns2开头的标签 * 提高xlsx读取兼容性:兼用ns2开头的标签

Loading…
Cancel
Save