easyexcel读取excel合并单元格数据

news/2024/7/21 7:32:20 标签: excel, listener, extra, merge, cellextra

    普通的excel列表,easyexcel读取是没有什么问题的。但是,如果有合并单元格,那么它读取的时候,能获取数据,但是数据是不完整的。如下所示的单元格数据:

    我们通过简单的异步读取,最后查看数据内容:

  ExcelData.java

package com.example.model;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExcelData {
    @ExcelProperty("学生姓名")
    private String name;
    @ExcelProperty("年龄")
    private int age;
    @ExcelProperty("性别")
    private String gender;

    @ExcelProperty({"课程", "课程名称"})
    private String courseName;

    @ExcelProperty({"课程", "分数"})
    private double score;
}

    ExcelRead.java

package com.example.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.model.ExcelData;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ExcelRead {
    private static final String FILEPATH = "e:\\test\\student.xlsx";

    public List<ExcelData> list() {
        List<ExcelData> excelDataList = new ArrayList<>();
        EasyExcel.read(FILEPATH, ExcelData.class, new AnalysisEventListener<ExcelData>() {
            @Override
            public void invoke(ExcelData excelData, AnalysisContext analysisContext) {
                log.info("read data {}", excelData);
                excelDataList.add(excelData);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {

            }
        }).sheet().doRead();
        return excelDataList;
    }
}

   ExcelTest.java

package com.example.service;

import com.example.model.ExcelData;

import java.util.List;

public class ExcelTest {
    public static void main(String[] args) {
        ExcelRead excelRead = new ExcelRead();
        List<ExcelData> list = excelRead.list();
        System.out.println(list.size());
    }
}

    运行程序,打印日志信息如下:

   获取了6个数据没错,但是每一个合并单元格记录里面都有一个数据获取是空的。

    解决办法就是需要在异步读取数据监听器里面读取合并单元格的额外数据,并把这部分数据给补充上。

    需要修改的地方:

    1、实体需要增加注解索引值:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExcelData {
    @ExcelProperty(value = "学生姓名", index = 0)
    private String name;
    @ExcelProperty(value = "年龄", index = 1)
    private int age;
    @ExcelProperty(value = "性别", index = 2)
    private String gender;

    @ExcelProperty(value = {"课程", "课程名称"}, index = 3)
    private String courseName;

    @ExcelProperty(value = {"课程", "分数"}, index = 4)
    private double score;
}

   2、自定义监听器,读取合并单元格数据:

package com.example.service;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellExtra;
import com.example.model.ExcelData;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class CustomAnalysisEventListener extends AnalysisEventListener<ExcelData> {

    private int headRowNum;

    public CustomAnalysisEventListener(int headRowNum) {
        this.headRowNum = headRowNum;
    }

    private List<ExcelData> list = new ArrayList<>();

    private List<CellExtra> cellExtraList = new ArrayList<>();

    @Override
    public void invoke(ExcelData excelData, AnalysisContext analysisContext) {
        log.info(" data -> {}", excelData);
        list.add(excelData);
    }

    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        CellExtraTypeEnum type = extra.getType();
        switch (type) {
            case MERGE: {
                if (extra.getRowIndex() >= headRowNum) {
                    cellExtraList.add(extra);
                }
                break;
            }
            default:{
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    public List<ExcelData> getList() {
        return list;
    }

    public List<CellExtra> getCellExtraList() {
        return cellExtraList;
    }
}

    3、通过监听器读取数据,通过监听器获取数据和合并单元格数据,然后设置单元格数据。


package com.example.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.metadata.CellExtra;
import com.example.model.ExcelData;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.List;


@Slf4j
public class ExcelRead {

    private static final int HEAD_ROW_NUM = 2;
    private static final String FILEPATH = "e:\\test\\student.xlsx";

    public List<ExcelData> list() {
        List<ExcelData> excelDataList;
        CustomAnalysisEventListener listener = new CustomAnalysisEventListener(HEAD_ROW_NUM);
        EasyExcel.read(FILEPATH, ExcelData.class, listener).extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();
        excelDataList = listener.getList();
        List<CellExtra> cellExtraList = listener.getCellExtraList();
        if (cellExtraList != null && cellExtraList.size() > 0) {
            mergeExcelData(excelDataList, cellExtraList, HEAD_ROW_NUM);
        }
        return excelDataList;
    }

    private void mergeExcelData(List<ExcelData> excelDataList, List<CellExtra> cellExtraList, int headRowNum) {
        cellExtraList.forEach(cellExtra -> {
            int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNum;
            int lastRowIndex = cellExtra.getLastRowIndex() - headRowNum;
            int firstColumnIndex = cellExtra.getFirstColumnIndex();
            int lastColumnIndex = cellExtra.getLastColumnIndex();
            //获取初始值
            Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, excelDataList);
            //设置值
            for (int i = firstRowIndex; i <= lastRowIndex; i++) {
                for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {
                    setInitValueToList(initValue, i, j, excelDataList);
                }
            }
        });
    }

    private void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<ExcelData> data) {
        ExcelData object = data.get(rowIndex);

        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == columnIndex) {
                    try {
                        field.set(object, filedValue);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的值异常:{}", e.getMessage());
                    }
                }
            }
        }
    }

    private Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<ExcelData> data) {
        Object filedValue = null;
        ExcelData object = data.get(firstRowIndex);
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == firstColumnIndex) {
                    try {
                        filedValue = field.get(object);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("设置合并单元格的初始值异常:{}", e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }
}

    有个小细节需要注意,我们在通过监听器读取的时候,还需要额外读取合并单元格部分。

EasyExcel.read(FILEPATH, ExcelData.class, listener).extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();     

    还有个小细节,就是我们的表格一般都是有头的,头的内容可能是2行,可能是1行,在进行合并单元格处理的时候,需要考虑这个行数。我们定义了一个常量HEAD_ROW_NUM来记录这个行数,最后进行单元格值计算的时候传入。 

    我在处理excel数据中发现,如果这个单元格合并,是由我们的程序自己做的,那么它读取的时候,是没有问题的。在上面为null的地方,它其实都有值:

    但是在实际中,我们的excel不能保证不被人为编辑,那么就很有可能,我们在进行合并单元格的时候,把有值和无值合并到一起,最后就出现前面提到的读取合并单元格出现数据缺失的问题。 而我们期望,合并单元格部分他们的数据应该是一样的。所以今天的解决方案是一种保险的做法,不管你的表格是否有合并单元格,是否人为修改,最终都能把合并单元格缺失的数据进行恢复。


http://www.niftyadmin.cn/n/274373.html

相关文章

【VM服务管家】VM4.x算法模块开发_4.3 联合Halcon开发

目录 4.3.1 联合开发&#xff1a;集成HALCON第三方算子到VM工具箱的方法 4.3.1 联合开发&#xff1a;集成HALCON第三方算子到VM工具箱的方法 描述 环境&#xff1a;VM4.0及以上 VS2013 问题&#xff1a;有的用户在使用VisionMaster软件在开发视觉项目时&#xff0c;可能同时也…

区块链学习教程大纲

区块链学习教程大纲 第一章&#xff1a;区块链基础 1.1 区块链概述 区块链的定义和特点 区块链的发展历程和趋势 1.2 区块链结构和原理 区块链的结构和组成部分 区块链的工作原理和机制 1.3 区块链应用场景 区块链的应用场景和领域 区块链在金融、物联网等领域的应用…

线性表之单链表(详解)

&#x1f355;博客主页&#xff1a;️自信不孤单 &#x1f36c;文章专栏&#xff1a;数据结构与算法 &#x1f35a;代码仓库&#xff1a;破浪晓梦 &#x1f36d;欢迎关注&#xff1a;欢迎大家点赞收藏关注 文章目录 &#x1f365;前言&#x1f349;链表1. 链表的概念及结构2. 链…

直播带货依然是一种平台经济,与电商并没有本质上的区别

无论是以阿里、京东和拼多多为代表的电商平台&#xff0c;还是以抖音、快手为代表的短视频平台&#xff0c;这些平台上的直播带货依然是平台经济。虽然我们看到了很多的头部主播拥有了庞大的粉丝群体&#xff0c;但是&#xff0c;这些粉丝和流量依然还是在这些平台上面&#xf…

Scala的高级用法

文章目录 1. 默认参数值1.1 方法默认参数1.2 类默认参数 2. 特质 (Traits)2.1 子类型2.2 扩展特征&#xff0c;当做接口来使用 3.元组3.1 定义与取值3.2 元组用于模式匹配3.3 用于for循环 4 高阶函数4.1 常见的高阶函数map4.2 简化涨薪策略代码 5.嵌套方法6.多参数列表&#xf…

【五一创作】Scratch资料袋

Scratch软件是免费的、免费的、免费的。任何需要花钱才能下载Scratch软件的全是骗子。 1、什么是Scratch Scratch是麻省理工学院的“终身幼儿园团队”开发的一种图形化编程工具。是面向青少年的一款模块化&#xff0c;积木化、可视化的编程语言。 什么是模块化、积木化&…

22:将成员变量声明为private

一、为什么成员变量不能是public 1.用户不必区分public接口内的哪些是函数&#xff0c;哪些是变量 若成员变量不是public&#xff0c;则用户唯一能访问对象的办法就是通过成员函数。 若public接口内的每样东西都是函数&#xff0c;则用户就不必记忆哪些是函数&#xff0c;哪…

二叉树的遍历及相关衍生

二叉树的遍历及相关衍生 前言二叉树的遍历建树二叉树的遍历遍历的分类代码部分 遍历根的应用打印树中的每个数据代码部分 遍历计算树节点个数代码部分 计算二叉树的深度思路代码部分 第k层个数 结束 前言 如标题所示&#xff0c;在这里我们要研究的是二叉树的遍历。 为什么不…