easyexcel读取excel合并单元格数据

news2024/11/28 16:32:03

    普通的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.coloradmin.cn/o/474374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

symfonos 2

目录 扫描 SMB SSH 提权 扫描 由于端口80是打开的,我们试图在浏览器中打开IP地址,但在网页上没有找到任何有用的信息。我们还尝试了dirb和其他目录暴力工具,但没有找到任何东西。 SMB 为了进一步枚举,我们使用Enum4Linux工具并找到了一些有用的信息。我们发现了一个名…

Microelectronic学习章节总结(1)-- 计算机架构复习

文章目录 Part1. 处理器架构&#xff0c;以及流水线的实现方法part2 DLX架构part3 ULTRA SPARC T2架构part4 PENTIUM 4架构part5 不同架构之间的性能比较 PPT&#xff1a;2&#xff0c;4&#xff0c;5&#xff0c;6 这一章主要对之前的计算机架构一些知识进行复习&#xff0c;因…

数字中国建设峰会|大模型带来产业智能化新机遇

第六届数字中国建设峰会在福建省福州市举办。峰会期间&#xff0c;百度与福州市政府签署战略协议&#xff0c;将基于文心一言为代表的大模型和百度智能云通用AI能力深入合作。未来&#xff0c;双方将聚焦算力产业&#xff0c;共建百度智能云&#xff08;福州&#xff09;智算中…

移动推车定位查找方案

CK_Label_v24 产品型号 CK_Label_v24 尺寸 124x90x12mm&#xff08;不含安装支架&#xff09; 屏幕尺寸 4.2 inch 显示技术 电子墨水屏显示 显示区域面积 (mm) 84.8(H) x 63.6(V) 分辨率 400*300 像素密度 120dpi 显示颜色 黑/白 外观颜色 白色&灰外圏…

Mozilla 自身是如何模糊 Firefox 浏览器的?

导语&#xff1a;Mozilla一直在模糊Firefox及其底层组件&#xff0c;它已被证明是识别质量和安全漏洞的最有效方法之一。 Mozilla一直在模糊Firefox及其底层组件&#xff0c;它已被证明是识别质量和安全漏洞的最有效方法之一。通常&#xff0c;研究人员会在不同级别上应用模糊测…

二叉树的遍历及相关衍生

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

java获取星期几

如果你要问 java什么时候学习比较好&#xff0c;那么答案肯定是 java的星期几。 在 Java中&#xff0c;你可以使用 public static void main &#xff08;&#xff09;方法来获取一个类的所有成员变量&#xff0c;然后在所有类中调用这个方法来获取对象的所有成员变量。它能以对…

MCSM面板一键搭建我的世界服务器-外网远程联机【内网穿透】

文章目录 前言1.Mcsmanager安装2.创建Minecraft服务器3.本地测试联机4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射内网端口 5.远程联机测试6. 配置固定远程联机端口地址6.1 保留一个固定TCP地址6.2 配置固定TCP地址 7. 使用固定公网地址远程联机 转载自远程穿透文章&…

【C++】模拟实现map和set

1.关联式容器 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是结构的 键值对&#xff0c;在数据检索时比序列式容器效率更高。 2 .键值对 用来表示具有一一对应关系的一种结构&#xff0c;该结构中一般只包含两个成员变量key和val…

2023-04-16 学习记录--C/C++-邂逅C/C++(上)

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、固定格式 ⭐️ stdio的理解: abbr.标准输入输出&#xff08;standard input/output&#xff09;。 #include <stdio.h>…

【LeetCode:72. 编辑距离 | 暴力递归=>记忆化搜索=>动态规划 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【Linux篇】Shell脚本语法

前言 在安卓源码里&#xff0c;离不开两个东西&#xff0c;一个就是.sh文件&#xff0c;还有一个就是.mk文件。 这两个文件各持有一个语法&#xff0c;一个是Makefile语法&#xff0c;一个是Shell脚本语法。 这两个是真的让我头疼&#xff0c;就像看天书一样&#xff0c;呜呜…

Mysql为json字段创建索引的两种方式

目录 一、前言二、通过虚拟列添加索引&#xff08;Secondary Indexes and Generated Columns&#xff09;三、多值索引&#xff08;Using multi-valued Indexes&#xff09;四、官网地址 一、前言 JSON 数据类型是在mysql5.7版本后新增的&#xff0c;同 TEXT&#xff0c;BLOB …

国内首款多节点/无需密钥/无需登录的ChatGPT客户端开源项目

在这个AI浪潮推动下&#xff0c;涌现了一大批“参差不齐”的GPT产品&#xff0c;有的一直在更新迭代&#xff0c;有的不断升级乃至付费订阅&#xff0c;有的中途停止运营。在这个AI产品也需要优胜劣汰的时代下&#xff0c;谁能够“谁主沉浮&#xff0c;且看今朝&#xff01;”&…

Ansible的基础了解

目录 第一章.Ansible概述 1.1.Ansible是什么 1.2.Ansible的特性和过程 1.3.ansible 具有如下特点&#xff1a; 1.4.Ansible的四个组件 1.5.ansible 核心程序 1.6.ansible执行的过程 第二章.Ansible 环境安装部署 2.1.实验环境&#xff0c;安装部署 第三章.ansible 命…

BLE调制与解调的一些东西

BLE调制 BLE是GFSK的IQ调制 IQ调制 所谓IQ调制&#xff0c;就是利用IQ两个分量序列去控制两路正交信号&#xff0c;I和Q两个序列可以是任意数字&#xff0c;也可以是符合某些规律的序列。 总的原理公式就是&#xff1a; cos(ab)cos(a)cos(b)-sin(a)sin(b) Acos(b)-Bsin(b)M…

音视频 FFmpeg

文章目录 前言视频编解码硬件解码(高级)软解码(低级)软、硬解码对比视频解码有四个步骤Android 系统中编解码器的命名方式查看当前设备支持的硬解码 基础知识RGB色彩空间常见的格式对比YUV索引格式分离RGB24像素数据中的R、G、B分量 BMP 文件格式格式组成像素排列顺序RGB24格式…

双指针技巧总结

一、双指针技巧——情景1 通常&#xff0c;我们只需要一个指针进行迭代&#xff0c;即从数组中的第一个元素开始&#xff0c;最后一个元素结束。然而&#xff0c;有时我们会使用两个指针进行迭代。 双指针的典型场景 (1)从两端向中间迭代数组。 (2)一个指针从头部开始&#…

Spark RDD (Resilient Distributed Datasets) 弹性分布式数据集

设计需求 Spark RDD 的设计目的是为了实现快速、可扩展和容错的数据处理。它是一个不可变的分布式数据集&#xff0c;可以在集群中分布式存储和处理数据。RDD 提供了一系列操作来处理数据&#xff0c;包括转换操作和行动操作。转换操作可以将一个 RDD 转换为另一个 RDD&#x…