Excel文件转java对象的工具类

废话不多说上代码!

maven

注意:5.1.0和5.2.0版本的优点问题,目前只能暂时先使用5.0.0版本

        <!--Excel处理-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.0.0</version>
        </dependency>

Excel Cell注解:

package com.excel;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * excel单元格
 * @author YanZhen
 * @date 2022/01/25 14:27
 */
@Target(FIELD)
@Retention(RUNTIME)
public @interface ExcelCell {
    /**
     * 列名,与Excel中的标题对应
     * @return 名称
     */
    String title() default "";

    /**
     * 目前只支持日期类型(Date、LocalDateTime)
     * 默认:yyyy-MM-dd HH:mm:ss
     *
     * @return 字段编号
     */
    String fieldFormat() default "";
}

Excel Util:

package com.excel;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * Excel解析工具类。
 * 目前可解析sheet的第一行为标题,且所有sheet的第一行的标题字段都相同,标题字段位置可以不同
 *
 * @author YanZhen
 * @date 2022/01/25 11:45
 */
@Slf4j
public class ExcelUtil {

    /**
     * 标题对应的字段信息
     */
    private static Map<String, Field> titleFields;

    /**
     * 字段的格式
     */
    private static final Map<String, String> fieldFormat = new HashMap<>();

    /**
     * 解析Excel数据转换为指定对象
     *
     * @param inputStream Excel文件流。注意:方法中会关闭流,所以不能重复使用
     * @param <T>         clazz 转换的对象类型
     * @return 转换后的数据
     */
    public static <T> List<T> getSheetToObject(InputStream inputStream, Class<T> clazz) {
        // 解析类的标题和字段的对应关系
        titleFields = getTitleField(clazz);
        if (titleFields.isEmpty()) {
            return null;
        }

        // sheet数据
        return getSheet(inputStream, clazz);
    }

    /**
     * 解析Excel的第一个sheet的数据,并转换为指定对象
     *
     * @param inputStream Excel文件流。注意:方法中会关闭流,所以不能重复使用
     * @param <T>         clazz 要转换的对象类型
     * @return 解析后的对象数据
     */
    public static <T> List<List<T>> getSheetListToObject(InputStream inputStream, Class<T> clazz) {
        // 解析类的标题和字段的对应关系
        titleFields = getTitleField(clazz);
        if (titleFields.isEmpty()) {
            return null;
        }

        // sheet数据,转换对象
        return getSheetList(inputStream, clazz);
    }

    /**
     * Sheet
     * 获取sheet的数据
     *
     * @param inputStream Excel文件流
     * @param <T>         clazz 要转换的对象类型
     * @return 解析后的对象数据
     */
    @Nullable
    private static <T> List<T> getSheet(InputStream inputStream, Class<T> clazz) {
        // 获取所有的sheet信息
        Workbook sheets = getWorkbook(inputStream);
        if (sheets == null) return null;

        // 只获取第一个sheet
        Sheet sheet = sheets.getSheetAt(0);
        if (sheet == null) {
            return null;
        }

        // 行数据
        return getRows(clazz, sheet);
    }

    /**
     * Sheet
     * 获取sheet的数据
     *
     * @param inputStream Excel文件流
     * @param <T>         clazz 要转换的对象类型
     * @return 解析后的对象数据
     */
    @Nullable
    private static <T> List<List<T>> getSheetList(InputStream inputStream, Class<T> clazz) {
        // 想要得到的数据
        List<List<T>> sheetList = new ArrayList<>();
        Workbook sheets = getWorkbook(inputStream);
        if (sheets == null) return null;

        // sheet
        for (Sheet sheet : sheets) {
            if (sheet == null) {
                continue;
            }

            // 行数据
            List<T> rows = getRows(clazz, sheet);
            if (rows == null) return null;

            // sheet数据
            sheetList.add(rows);
        }
        return sheetList;
    }

    /**
     * 获取Excel的所有sheet的数据
     *
     * @param inputStream Excel文件流
     * @return sheet对象
     */
    @Nullable
    private static Workbook getWorkbook(InputStream inputStream) {
        Workbook sheets;

        // 解析Excel数据
        try {
            sheets = WorkbookFactory.create(inputStream);
            inputStream.close();
        } catch (IOException e) {
            log.error("解释Excel异常", e);
            return null;
        }
        return sheets;
    }

    /**
     * Row
     * 获取行数据
     *
     * @param <T>   clazz 对象类型
     * @param sheet sheet中的信息
     * @return 行数据
     */
    @Nullable
    private static <T> List<T> getRows(Class<T> clazz, Sheet sheet) {
        // 标题对应索引
        Map<String, Integer> titleIndexMap = new HashMap<>();
        // 行信息
        List<T> rows = new ArrayList<>();

        // 行
        for (Row cells : sheet) {
            if (cells == null) {
                continue;
            }

            // 根据行标,解析第一行为标题
            if (cells.getRowNum() == 0) {
                titleIndexMap = getTitle(cells);
                continue;
            }
            // 不能没有标题,否则无法转对应的数据
            if (titleIndexMap.isEmpty()) {
                return Lists.newArrayList();
            }

            // 初始化对象信息
            T t;
            try {
                t = clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                log.error("初始化对象信息异常!", e);
                return null;
            }

            // 根据标题对应的索引信息,解析单元格数据
            titleIndexMap.forEach((title, cellNum) -> {
                // 获取单元格的信息
                Cell cell = cells.getCell(cellNum);

                // 根据对象的字段信息赋值
                titleFields.forEach((title1, field) -> {
                    // 标题是否匹配
                    if (!Objects.equals(title, title1)) {
                        return;
                    }
                    // 为对象字段赋值
                    try {
                        initObject(t, cell, field);
                    } catch (IllegalAccessException e) {
                        log.error("赋值对象的{}值发生异常!", field.getName(), e);
                    }
                });
            });
            // 行数据
            rows.add(t);
        }
        return rows;
    }

    /**
     * 初始化对象信息
     *
     * @param t     对象实例
     * @param cell  单元格信息
     * @param field 对象字段信息
     * @param <T>   对象类型
     */
    private static <T> void initObject(T t, Cell cell, Field field) throws IllegalAccessException {
        // 单元格的值
        Object value = getCellValue(cell, field);
        // 允许私有字段赋值
        field.setAccessible(true);
        // 字段赋值
        field.set(t, value);
    }

    /**
     * 获取单元格值
     *
     * @param cell  单元格信息
     * @param field 字段信息
     * @return 单元格值
     */
    @Nullable
    private static Object getCellValue(Cell cell, Field field) {
        if (cell == null) {
            return null;
        }

        // 获取字段的类型
        Class<?> type = field.getType();
        Object value = null;

        /*
         * 默认日期格式类型
         */
        String defaultDateFormat = "yyyy-MM-dd HH:mm:ss";
        // String
        if (type.equals(String.class)) {
            value = cell.toString();

            // 数值类型
        } else if (type.getSuperclass().equals(Number.class)) {
            value = getObjToNumber(cell, type);

            // boolean
        } else if (type.equals(Boolean.class)) {
            value = cell.getBooleanCellValue();

            // 日期类型
        } else if (type.equals(Date.class)) {
            try {
                // 日期字段格式
                String format = fieldFormat.getOrDefault(field.getName(), defaultDateFormat);
                value = new SimpleDateFormat(format).parse(cell.toString());
            } catch (ParseException e) {
                log.error("{}字段类型({}),日期格式化失败!", field.getName(), type, e);
            }
        } else if (type.equals(LocalDateTime.class)) {
            // 日期字段格式
            String format = fieldFormat.getOrDefault(field.getName(), defaultDateFormat);
            value = LocalDateTime.parse(cell.toString(), DateTimeFormatter.ofPattern(format));

            // 没有匹配到的情况
        } else {
            log.warn("{}字段类型({})没有匹配到,忽略赋值。单元格实际值为:{}", field.getName(), type, cell.getStringCellValue());
        }
        return value;
    }

    /**
     * 解析单元格值
     * @param cell 单元格信息
     * @param type 目标类型
     * @return 解析后的对应类型的值
     */
    private static Object getObjToNumber(Cell cell, Class<?> type) {
        // 空时,默认返回空
        if (cell == null || type == null) {
            return null;
        }

        // 单元格值转字符串
        String s = cell.toString();
        if (StringUtils.isBlank(s)) {
            return null;
        }
        // 去两边空格
        s = s.trim();
        // 数值类型校验
        if (!Pattern.matches("^-?(\\d+).?(\\d+)?$", s)) {
            throw new IllegalArgumentException(String.format(
                    "格式错误,单元格值(%s)为非数值类型,不能赋值给数值类型(%s)的对象", s, type.getName()));
        }

        // 类型匹配
        if (type.equals(Double.class) || type.equals(double.class)) {
            return Double.parseDouble(s);
        } else if (type.equals(Float.class) || type.equals(float.class)) {
            return Float.parseFloat(s);
        } else if (type.equals(Integer.class) || type.equals(int.class)) {
            return Integer.parseInt(s);
        } else if (type.equals(Short.class) || type.equals(short.class)) {
            return Short.parseShort(s);
        } else if (type.equals(Byte.class) || type.equals(byte.class)) {
            return Byte.parseByte(s);
        } else {
            log.warn("暂时不支持复杂类型({})的转化,值转化失败:{}", type.getName(), s);
            return null;
        }
    }

    /**
     * 解析标题名和字段的对应关系
     *
     * @param clazz 对象
     * @param <T>   对象类型
     * @return 标题名和字段名
     */
    private static <T> Map<String, Field> getTitleField(Class<T> clazz) {
        Map<String, Field> titleFieldMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(ExcelCell.class)) {
                continue;
            }
            // 获取字段代表的单元格标题
            ExcelCell excelCell = field.getDeclaredAnnotation(ExcelCell.class);
            titleFieldMap.put(excelCell.title(), field);
            // 字段格式
            if (StringUtils.isNotBlank(excelCell.fieldFormat())) {
                fieldFormat.put(field.getName(), excelCell.fieldFormat());
            }
        }
        return titleFieldMap;
    }

    /**
     * 解析标题信息
     *
     * @param cells 行数据
     * @return 标题对应的索引信息
     */
    private static Map<String, Integer> getTitle(Row cells) {
        Map<String, Integer> titles = new HashMap<>();
        for (Cell cell : cells) {
            if (cell == null) {
                continue;
            }
            // 列索引
            int columnIndex = cell.getColumnIndex();
            // 列值
            String stringCellValue = cell.getStringCellValue();

            titles.put(stringCellValue, columnIndex);
        }
        return titles;
    }

    public static void main(String[] args) throws FileNotFoundException {
//        File file = new File("/Users/**/模板.xlsx");
//        InputStream fileInputStream = new FileInputStream(file);
        // 单个sheet解析
//        List<Abc> sheetToObject = ExcelUtil.getSheetToObject(fileInputStream, Abc.class);
//        log.info("结果1:{}", sheetToObject);
//        System.out.println("结果2:" + sheetToObject);

        File file = new File("/Users/**/模板.xls");
        InputStream fileInputStream = new FileInputStream(file);
        // 多个sheet解析
        List<List<Abc>> sheetsToObject = ExcelUtil.getSheetListToObject(fileInputStream, Abc.class);
        log.info("结果3:{}", sheetsToObject);
        System.out.println("结果4:" + sheetsToObject);
    }

    @Data
    public static class Abc {
        @ExcelCell(title = "A")
        private String a;
        @ExcelCell(title = "B")
        private String b;
        @ExcelCell(title = "C")
        private Integer c;
        @ExcelCell(title = "D", fieldFormat = "yyyy-MM-dd")
        private Date d;
    }

}

 Excel:

格式:xlsx、xls。(最好xlsx)

最好都设置成文本格式,代码支持 字符串、数值、布尔值、日期 等格式

sheet1: 

sheet2

 结果:

/Library/Java/JavaVirtualMachines/jdk-11.0.12.jdk/Contents/Home/bin/java ...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.


结果4:[[ExcelUtil.Abc(a=a, b=b, c=c, d=Tue Jan 25 00:00:00 CST 2022), ExcelUtil.Abc(a=1, b=2, c=3, d=Tue Jan 25 00:00:00 CST 2022), ExcelUtil.Abc(a=1, b=2, c=3, d=Tue Jan 25 00:00:00 CST 2022), ExcelUtil.Abc(a=null, b=4, c=null, d=null)], [ExcelUtil.Abc(a=a, b=b, c=c, d=Tue Jan 25 00:00:00 CST 2022), ExcelUtil.Abc(a=1, b=2, c=3, d=Tue Jan 25 00:00:00 CST 2022)]]

Process finished with exit code 0

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
很高兴能够回答这个问题。可以使用Apache POI库来实现Java编写导入导出Excel文件工具类。该库可以对Excel文件进行读写操作,支持Excel 97-2003和Excel 2007及以上格式的文件。以下是一个简单的示例代码,可以参考: ``` import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.util.List; public class ExcelUtils { /** * 导出Excel文件 * * @param header Excel表头 * @param data Excel数据 * @param path 文件路径 * @throws Exception */ public static void exportExcel(List<String> header, List<List<Object>> data, String path) throws Exception { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); // 写入表头 Row headerRow = sheet.createRow(0); for (int i = 0; i < header.size(); i++) { headerRow.createCell(i).setCellValue(header.get(i)); } // 写入数据 for (int i = 0; i < data.size(); i++) { Row row = sheet.createRow(i + 1); List<Object> rowData = data.get(i); for (int j = 0; j < rowData.size(); j++) { Object value = rowData.get(j); if (value instanceof Number) { row.createCell(j).setCellValue(((Number) value).doubleValue()); } else { row.createCell(j).setCellValue(value.toString()); } } } // 保存文件 try (FileOutputStream outputStream = new FileOutputStream(path)) { workbook.write(outputStream); } } } ``` 以上代码使用了XSSFWorkbook对象来创建一个新的Excel文件。将表头和数据写入工作表中,并将保存到指定路径的文件中。请注意,此示例代码仅用于说明如何在Java中编写导入导出Excel文件工具类,实际应用场景可能需要根据具体要求进行更改和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值