Loading

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);
    }
}

应用

  1. 是否枚举
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;
    }
}
  1. 是否枚举转换器
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> {
}

  1. 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;
}
  1. 测试类
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());
        }
    }
    

}
posted @ 2025-08-11 15:48  IamHzc  阅读(124)  评论(0)    收藏  举报