EasyExcel 导入/出通用枚举映射
描述:在进行导入导出操作时经常需要进行一些枚举转换,利用EasyExcel的转换器,封装公共方法,简化这一操作。
maven依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.24.3</version>
</dependency>
<!-- JUnit 4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
公共枚举和转换器
枚举
package com.easy.excel.convert.enums;
import java.util.Objects;
/**
* <p>Title: 通用基础枚举接口</p>
* <p>Description: 定义枚举类的通用行为,提供code与desc之间的转换功能</p>
*
* @author Ryan
* @param <T> code的类型参数
*/
public interface BaseCodeDescEnum<T> {
/**
* 获取枚举code值
* @return code值
*/
T getCode();
/**
* 获取枚举描述信息
* @return 描述信息
*/
String getDesc();
/**
* 根据code获取desc
* @param enumClass 枚举类class
* @param code code值
* @param <E> 实现BaseCodeDescEnum的枚举类型
* @param <T> code的类型参数
* @return 对应的desc,未找到返回空字符串
*/
static <T, E extends Enum<E> & BaseCodeDescEnum<T>> String getDescByCode(Class<E> enumClass, T code) {
if (code == null) {
return "";
}
for (E e : enumClass.getEnumConstants()) {
if (Objects.equals(e.getCode(), code)) {
return e.getDesc();
}
}
return "";
}
/**
* 根据desc获取code
* @param enumClass 枚举类class
* @param desc 描述信息
* @param <E> 实现BaseCodeDescEnum的枚举类型
* @param <T> code的类型参数
* @return 对应的code,未找到返回null
*/
static <T, E extends Enum<E> & BaseCodeDescEnum<T>> T getCodeByDesc(Class<E> enumClass, String desc) {
if (desc == null) {
return null;
}
for (E e : enumClass.getEnumConstants()) {
if (Objects.equals(e.getDesc(), desc)) {
return e.getCode();
}
}
return null;
}
}
转换器
package com.easy.excel.convert;
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;
import com.easy.excel.convert.enums.BaseCodeDescEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>Title: 通用枚举转换器</p>
* <p>Description: 用于Excel与Java枚举之间的数据转换,支持实现BaseCodeDescEnum接口的枚举类</p>
*
* @author Ryan
* @param <T> 枚举code的类型
* @param <E> 枚举类型
*/
public class CommonEnumConverter<T, E extends Enum<E> & BaseCodeDescEnum<T>> implements Converter<T> {
private final Logger log = LoggerFactory.getLogger(CommonEnumConverter.class);
private final Class<E> enumClass;
/**
* 无参构造函数,通过泛型获取枚举类型
* 适用于直接继承的子类,如SexConverter、YesOrNoConverter等
*/
public CommonEnumConverter() {
// 获取子类的泛型参数
this.enumClass = getEnumClassFromGeneric();
}
/**
* 有参构造函数,直接指定枚举类型
* 保留此构造函数以兼容现有代码
* @param enumClass 枚举类型
*/
public CommonEnumConverter(Class<E> enumClass) {
this.enumClass = enumClass;
}
/**
* 从泛型参数中获取枚举类型
* @return 枚举类型
*/
@SuppressWarnings("unchecked")
private Class<E> getEnumClassFromGeneric() {
try {
// 获取当前类的直接父类的类型信息
java.lang.reflect.Type type = getClass().getGenericSuperclass();
// 如果是参数化类型(即包含泛型参数的类型)
if (type instanceof java.lang.reflect.ParameterizedType) {
java.lang.reflect.ParameterizedType parameterizedType = (java.lang.reflect.ParameterizedType) type;
// 获取第二个泛型参数,即E的类型
java.lang.reflect.Type actualTypeArgument = parameterizedType.getActualTypeArguments()[1];
if (actualTypeArgument instanceof Class) {
return (Class<E>) actualTypeArgument;
}
}
throw new IllegalArgumentException("无法获取枚举类型,请确保正确继承CommonEnumConverter并指定泛型参数");
} catch (Exception e) {
throw new IllegalArgumentException("获取枚举类型失败: " + e.getMessage(), e);
}
}
@Override
public Class<?> supportJavaTypeKey() {
// 获取第一个枚举常量的code类型
E[] constants = enumClass.getEnumConstants();
if (constants != null && constants.length > 0) {
T code = constants[0].getCode();
return code != null ? code.getClass() : Object.class;
}
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* Excel数据转换为Java数据
* @param cellData Excel单元格数据
* @param excelContentProperty Excel内容属性
* @param globalConfiguration 全局配置
* @return 转换后的T类型值
*/
@Override
public T convertToJavaData(ReadCellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) {
if (cellData == null || cellData.getStringValue() == null) {
return null;
}
T code = BaseCodeDescEnum.getCodeByDesc(enumClass, cellData.getStringValue());
if (code == null) {
log.warn("无法找到枚举描述'{}'对应的code值,枚举类型: {}",
cellData.getStringValue(), enumClass.getSimpleName());
}
return code;
}
/**
* Java数据转换为Excel数据
* @param value Java中的T类型值
* @param excelContentProperty Excel内容属性
* @param globalConfiguration 全局配置
* @return 转换后的Excel单元格数据
*/
@Override
public WriteCellData<String> convertToExcelData(T value, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) {
if (value == null) {
return new WriteCellData<>("");
}
String desc = BaseCodeDescEnum.getDescByCode(enumClass, value);
if (desc == null || desc.isEmpty()) {
log.warn("无法找到枚举code'{}'对应的描述值,枚举类型: {}",
value, enumClass.getSimpleName());
return new WriteCellData<>("未知(" + value + ")");
}
return new WriteCellData<>(desc);
}
}
应用
- 是否枚举
package com.easy.excel.convert.enums;
import lombok.Getter;
/**
* <p>Title: 是否枚举</p>
* <p>Description: Function Description </p>
*
* @author Ryan
*/
@Getter
public enum YesOrNoEnum implements BaseCodeDescEnum<Integer> {
/**
* 0-否
*/
NO(0, "否"),
/**
* 1-是
*/
YES(1, "是");
private final Integer code;
private final String desc;
YesOrNoEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
- 是否枚举转换器
package com.easy.excel.convert;
import com.easy.excel.convert.enums.YesOrNoEnum;
/**
* <p>Title: 是否转换器</p>
* <p>Description: Function Description </p>
*
* @author Ryan
*/
public class YesOrNoConverter extends CommonEnumConverter<Integer, YesOrNoEnum> {
}
- DTO应用
@Data
public class TestData {
@ExcelProperty("ID")
private Integer id;
@ExcelProperty("姓名")
private String name;
@ExcelProperty(value = "是否有效", converter = YesOrNoConverter.class)
private Integer valid;
@ExcelProperty("年龄")
private Integer age;
}
- 测试类
package com.easy.excel.convert;
import com.alibaba.excel.EasyExcel;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* <p>Title: Excel转换器测试类</p>
* <p>Description: 用于测试Excel转换相关功能</p>
* @author Ryan
*/
@Slf4j
@DisplayName("Excel转换器测试")
public class ConvertTest {
@Test
@DisplayName("测试使用YesOrNoConverter导出Excel")
public void testYesOrNoExport() {
List<TestData> dataList = new ArrayList<>();
TestData data1 = new TestData();
data1.setId(1);
data1.setName("张三");
data1.setValid(1); // 是
data1.setAge(25);
dataList.add(data1);
TestData data2 = new TestData();
data2.setId(2);
data2.setName("李四");
data2.setValid(0); // 否
data2.setAge(30);
dataList.add(data2);
// 创建临时文件用于写入
File tempFile = new File("E:\\test_export.xlsx");
// 导出数据到Excel
EasyExcel.write(tempFile, TestData.class)
.sheet("测试数据")
.doWrite(dataList);
// 验证文件是否创建成功
assertTrue(tempFile.exists(), "Excel文件应该被成功创建");
log.info("Excel文件已创建成功:{}", tempFile.getAbsolutePath());
}
@Test
@DisplayName("测试使用YesOrNoConverter导入Excel")
public void testYesOrNoImport() {
// 创建临时文件用于测试导入
File tempFile = new File("E:\\test_export.xlsx");
// 读取数据并验证转换是否正确
List<TestData> readList = EasyExcel.read(tempFile)
.head(TestData.class)
.sheet()
.doReadSync();
for (TestData data : readList) {
log.info("ID: {}, Name: {}, Valid: {}, age: {}",
data.getId(), data.getName(), data.getValid(), data.getAge());
}
}
}