High-performance Java library for reading and writing Excel files with minimal memory usage
—
Type conversion system for transforming between Java objects and Excel cell values. EasyExcel provides a pluggable converter system that handles automatic type detection and custom conversion logic for complex data transformations.
Primary interface for implementing custom data converters.
/**
* Interface for converting between Java objects and Excel cell values
* @param <T> Java type that this converter handles
*/
public interface Converter<T> {
/**
* Get the Java type that this converter supports
* @return Class of supported Java type
*/
Class<?> supportJavaTypeKey();
/**
* Get the Excel cell data type that this converter supports
* @return CellDataTypeEnum of supported Excel type
*/
CellDataTypeEnum supportExcelTypeKey();
/**
* Convert Excel cell data to Java object (for reading)
* @param cellData Cell data from Excel
* @param contentProperty Content property configuration
* @param globalConfiguration Global configuration settings
* @return Converted Java object
* @throws Exception if conversion fails
*/
T convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception;
/**
* Convert Java object to Excel cell data (for writing)
* @param value Java object to convert
* @param contentProperty Content property configuration
* @param globalConfiguration Global configuration settings
* @return Cell data for Excel
* @throws Exception if conversion fails
*/
WriteCellData<?> convertToExcelData(T value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception;
}Pre-built converters for automatic type handling.
/**
* Automatic converter that detects appropriate converter based on type
*/
public class AutoConverter implements Converter<Object> {
@Override
public Class<?> supportJavaTypeKey() {
return null; // Supports all types
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null; // Supports all cell types
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
// Automatic type detection and conversion
return null; // Implementation handles detection
}
@Override
public WriteCellData<?> convertToExcelData(Object value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
// Automatic type detection and conversion
return null; // Implementation handles detection
}
}
/**
* Converter for handling null values
*/
public class NullableObjectConverter implements Converter<Object> {
@Override
public Class<?> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.EMPTY;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
if (cellData == null || cellData.getData() == null) {
return null;
}
// Delegate to appropriate converter
return cellData.getData();
}
@Override
public WriteCellData<?> convertToExcelData(Object value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
if (value == null) {
return new WriteCellData<>("");
}
// Delegate to appropriate converter
return new WriteCellData<>(value);
}
}Classes for loading and managing converters.
/**
* Default converter loader and registry
*/
public class DefaultConverterLoader {
/**
* Load all default converters
* @return Map of converter configurations
*/
public static Map<String, Converter<?>> loadDefaultReadConverter() {
// Returns map of all built-in read converters
return null; // Implementation loads converters
}
/**
* Load all default write converters
* @return Map of converter configurations
*/
public static Map<String, Converter<?>> loadDefaultWriteConverter() {
// Returns map of all built-in write converters
return null; // Implementation loads converters
}
/**
* Load converters for specific type
* @param clazz Java class to load converters for
* @return Map of converters for the class
*/
public static Map<String, Converter<?>> loadConverter(Class<?> clazz) {
// Returns converters specific to the class
return null; // Implementation loads converters
}
}
/**
* Converter holder for managing converter instances
*/
public class ConverterHolder {
/**
* Register a custom converter
* @param converter Converter to register
*/
public void registerConverter(Converter<?> converter);
/**
* Get converter for Java type and Excel type combination
* @param javaType Java class
* @param excelType Excel cell data type
* @return Appropriate converter or null
*/
public Converter<?> getConverter(Class<?> javaType, CellDataTypeEnum excelType);
/**
* Get all registered converters
* @return Map of all converters
*/
public Map<String, Converter<?>> getAllConverter();
}Classes representing cell data during conversion.
/**
* Cell data structure used during reading operations
* @param <T> Type of data contained in the cell
*/
public class ReadCellData<T> {
/**
* Cell data type
*/
private CellDataTypeEnum type;
/**
* Cell data value
*/
private T data;
/**
* Cell formula (if applicable)
*/
private String formula;
/**
* String representation of cell value
*/
private String stringValue;
/**
* Number representation of cell value
*/
private BigDecimal numberValue;
/**
* Boolean representation of cell value
*/
private Boolean booleanValue;
/**
* Date representation of cell value
*/
private Date dateValue;
// Getters and setters...
}
/**
* Cell data structure used during writing operations
* @param <T> Type of data to write to the cell
*/
public class WriteCellData<T> {
/**
* Cell data type
*/
private CellDataTypeEnum type;
/**
* Cell data value
*/
private T data;
/**
* Cell formula (if applicable)
*/
private String formula;
/**
* Data format for the cell
*/
private String dataFormat;
/**
* Data format index
*/
private Short dataFormatIndex;
// Constructors
public WriteCellData();
public WriteCellData(T data);
public WriteCellData(String formula, T data);
// Getters and setters...
}Classes providing configuration context for converters.
/**
* Excel content property containing field configuration
*/
public class ExcelContentProperty {
/**
* Field information
*/
private Field field;
/**
* Date format configuration
*/
private DateTimeFormatProperty dateTimeFormatProperty;
/**
* Number format configuration
*/
private NumberFormatProperty numberFormatProperty;
/**
* Custom converter class
*/
private Class<? extends Converter<?>> converter;
/**
* Column index
*/
private Integer index;
/**
* Column order
*/
private Integer order;
// Getters and setters...
}
/**
* Date/time format property
*/
public class DateTimeFormatProperty {
/**
* Date format pattern
*/
private String format;
/**
* Use 1904 date windowing
*/
private Boolean use1904windowing;
// Getters and setters...
}
/**
* Number format property
*/
public class NumberFormatProperty {
/**
* Number format pattern
*/
private String format;
/**
* Number format index
*/
private Short index;
// Getters and setters...
}import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
// Enum definition
public enum UserStatus {
ACTIVE("Active"),
INACTIVE("Inactive"),
PENDING("Pending"),
SUSPENDED("Suspended");
private final String displayName;
UserStatus(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
public static UserStatus fromDisplayName(String displayName) {
for (UserStatus status : values()) {
if (status.displayName.equals(displayName)) {
return status;
}
}
throw new IllegalArgumentException("Unknown status: " + displayName);
}
}
// Custom converter implementation
public class UserStatusConverter implements Converter<UserStatus> {
@Override
public Class<?> supportJavaTypeKey() {
return UserStatus.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public UserStatus convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellValue = cellData.getStringValue();
if (cellValue == null || cellValue.trim().isEmpty()) {
return null;
}
try {
return UserStatus.fromDisplayName(cellValue.trim());
} catch (IllegalArgumentException e) {
throw new ExcelDataConvertException(
"Cannot convert '" + cellValue + "' to UserStatus", e);
}
}
@Override
public WriteCellData<?> convertToExcelData(UserStatus value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
if (value == null) {
return new WriteCellData<>("");
}
return new WriteCellData<>(value.getDisplayName());
}
}
// Data class using custom converter
public class UserData {
@ExcelProperty("Name")
private String name;
@ExcelProperty(value = "Status", converter = UserStatusConverter.class)
private UserStatus status;
@ExcelProperty("Email")
private String email;
// Getters and setters...
}import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeConverter implements Converter<LocalDateTime> {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public Class<?> supportJavaTypeKey() {
return LocalDateTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDateTime convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellValue = cellData.getStringValue();
if (cellValue == null || cellValue.trim().isEmpty()) {
return null;
}
try {
return LocalDateTime.parse(cellValue.trim(), FORMATTER);
} catch (Exception e) {
throw new ExcelDataConvertException(
"Cannot parse date: " + cellValue, e);
}
}
@Override
public WriteCellData<?> convertToExcelData(LocalDateTime value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
if (value == null) {
return new WriteCellData<>("");
}
return new WriteCellData<>(value.format(FORMATTER));
}
}
// Usage in data class
public class EventData {
@ExcelProperty("Event Name")
private String name;
@ExcelProperty(value = "Event Date", converter = LocalDateTimeConverter.class)
private LocalDateTime eventDate;
@ExcelProperty("Description")
private String description;
// Getters and setters...
}import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
// Complex object to convert
public class Address {
private String street;
private String city;
private String zipCode;
private String country;
// Constructors, getters, setters...
@Override
public String toString() {
return street + ", " + city + ", " + zipCode + ", " + country;
}
}
// JSON-based converter for complex objects
public class AddressConverter implements Converter<Address> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public Class<?> supportJavaTypeKey() {
return Address.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Address convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellValue = cellData.getStringValue();
if (cellValue == null || cellValue.trim().isEmpty()) {
return null;
}
try {
// Try parsing as JSON first
if (cellValue.startsWith("{")) {
return objectMapper.readValue(cellValue, Address.class);
}
// Fallback to simple comma-separated format
String[] parts = cellValue.split(",");
if (parts.length >= 4) {
Address address = new Address();
address.setStreet(parts[0].trim());
address.setCity(parts[1].trim());
address.setZipCode(parts[2].trim());
address.setCountry(parts[3].trim());
return address;
}
throw new IllegalArgumentException("Invalid address format");
} catch (Exception e) {
throw new ExcelDataConvertException(
"Cannot convert address: " + cellValue, e);
}
}
@Override
public WriteCellData<?> convertToExcelData(Address value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
if (value == null) {
return new WriteCellData<>("");
}
// Write as JSON for full fidelity
try {
String json = objectMapper.writeValueAsString(value);
return new WriteCellData<>(json);
} catch (Exception e) {
// Fallback to string representation
return new WriteCellData<>(value.toString());
}
}
}import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;
public class CurrencyConverter implements Converter<BigDecimal> {
private final Currency currency;
private final Locale locale;
private final NumberFormat currencyFormat;
public CurrencyConverter() {
this(Currency.getInstance("USD"), Locale.US);
}
public CurrencyConverter(Currency currency, Locale locale) {
this.currency = currency;
this.locale = locale;
this.currencyFormat = NumberFormat.getCurrencyInstance(locale);
this.currencyFormat.setCurrency(currency);
}
@Override
public Class<?> supportJavaTypeKey() {
return BigDecimal.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING; // Handle formatted currency strings
}
@Override
public BigDecimal convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellValue = cellData.getStringValue();
if (cellValue == null || cellValue.trim().isEmpty()) {
return null;
}
try {
// Remove currency symbols and parse
String cleanValue = cellValue.replaceAll("[^\\d.,+-]", "");
return new BigDecimal(cleanValue);
} catch (Exception e) {
throw new ExcelDataConvertException(
"Cannot parse currency: " + cellValue, e);
}
}
@Override
public WriteCellData<?> convertToExcelData(BigDecimal value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
if (value == null) {
return new WriteCellData<>("");
}
String formattedValue = currencyFormat.format(value);
return new WriteCellData<>(formattedValue);
}
}
// Usage with custom currency
public class SalesData {
@ExcelProperty("Product")
private String product;
@ExcelProperty(value = "Price", converter = CurrencyConverter.class)
private BigDecimal price;
@ExcelProperty("Quantity")
private Integer quantity;
// Getters and setters...
}import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
// Register converters globally for reading
ExcelReaderBuilder readerBuilder = EasyExcel.read("data.xlsx", UserData.class, listener);
// Register custom converter (would be done through configuration in actual implementation)
// readerBuilder.registerConverter(new UserStatusConverter());
// For writing with custom converters
ExcelWriterBuilder writerBuilder = EasyExcel.write("output.xlsx", UserData.class);
// Register custom converter (would be done through configuration in actual implementation)
// writerBuilder.registerConverter(new UserStatusConverter());
writerBuilder.sheet("Users").doWrite(userData);import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
public class SmartNumberConverter implements Converter<Number> {
@Override
public Class<?> supportJavaTypeKey() {
return Number.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Number convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
String cellValue = cellData.getStringValue();
if (cellValue == null || cellValue.trim().isEmpty()) {
return null;
}
cellValue = cellValue.trim();
try {
// Detect if it's a percentage
if (cellValue.endsWith("%")) {
double value = Double.parseDouble(cellValue.substring(0, cellValue.length() - 1));
return value / 100.0; // Convert percentage to decimal
}
// Detect if it contains decimal point
if (cellValue.contains(".")) {
return Double.parseDouble(cellValue);
}
// Try as integer first
long longValue = Long.parseLong(cellValue);
if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) {
return (int) longValue;
}
return longValue;
} catch (NumberFormatException e) {
throw new ExcelDataConvertException(
"Cannot parse number: " + cellValue, e);
}
}
@Override
public WriteCellData<?> convertToExcelData(Number value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws Exception {
if (value == null) {
return new WriteCellData<>("");
}
// Format based on type
if (value instanceof Double || value instanceof Float) {
BigDecimal bd = BigDecimal.valueOf(value.doubleValue());
return new WriteCellData<>(bd.toPlainString());
}
return new WriteCellData<>(value.toString());
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-alibaba--easyexcel