当前位置: 首页 > news >正文

easyExcel 不规则模板导入数据

文章目录

  • 前言
  • 一、需求和效果
  • 二、难点和思路
  • 三、全部代码
  • 踩坑


前言

之前分享的 EasyExcel 批量导入并校验数据,仅支持规则excel,即首行表头,下面对应数据,无合并单元格情况。
本篇主要解决问题:

  1. 模板excel 表头不在首行
  2. 数据项有合并单元格情况

esayexcel版本2.2.7

一、需求和效果

在这里插入图片描述
在这里插入图片描述

二、难点和思路

  1. 跳过表头前的说明

    设置headRowNumber指定表头位置,第三行

EasyExcel.read(inputStream, EvalTemplateReq.class,listener).extraRead(CellExtraTypeEnum.MERGE).sheet().headRowNumber(3).doRead();
  1. 合并单元格数据获取放入list
    合并单元格的数据默认是在合并表格的左上第一个格子,其他为null,获取到这个格子的数据,赋值给其他被合并单元格对应的字段中

3.1 开启合并单元格识别
extraRead(CellExtraTypeEnum.MERGE)开启合并单元格识别
3.2 获取合并的单元格数据
EvalExcelDataListener 重写extra方法,获取到被合并的数据,后续处理。extra方法在invoke方法执行,先不要在意要导入的list部分字段是null,后续重新赋值即可。
extra会从excel头读取,所以要把前2行的数据过滤掉,这里用extra.getFirstRowIndex()>2判断一下

    // 合并单元格private final List<CellExtra> extraMergeInfoList = new ArrayList<>();@Overridepublic void extra(CellExtra extra, AnalysisContext context) {if (extra.getType() == CellExtraTypeEnum.MERGE) {// 处理合并单元格的情况int firstRowIndex = extra.getFirstRowIndex();if(firstRowIndex>2) {extraMergeInfoList.add(extra);}}}

3.3 doAfterAllAnalysed中补全list字段值

    @Overridepublic void doAfterAllAnalysed(AnalysisContext context) {//所有数据解析完毕执行该方法// 防止导入空的Excelif (context.readRowHolder().getRowIndex() <= 0) {throw new ExcelAnalysisException("当前excel无数据!");}//处理合并单元格list = EasyExcelMergeUtil.explainMergeData(list, extraMergeInfoList, 3);saveData();}

3.4 explainMergeData 补全字段值
根据单元格左上角标通过反射获取字段并赋值给其他被合并的单元格,角标对应实体类index属性

三、全部代码

  1. 接收数据实体
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.validation.constraints.NotBlank;
import java.util.List;/*** 评估标准*/
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EvalTemplateReq {@ExcelIgnoreprivate String id;/*** 评估项*/@ExcelProperty(value = "评估名称",index = 1)@NotBlank(message = "评估名称不能为空")private String item;/*** 指标简称*/@ExcelProperty(value = "指标简称",index =2)@NotBlank(message = "指标简称不能为空")private String itemShortName;/*** 指标编码*/@ExcelProperty(value = "指标编码",index = 3)@NotBlank(message = "指标编码不能为空")private String itemCode;/*** 指标释义*/@ExcelProperty(value = "指标释义",index = 4)@NotBlank(message = "指标释义不能为空")private String itemExplain;/*** 评价要求/分数 json*/@ExcelIgnoreprivate List<Requirements> requirementList;/*** 系统监测类/参演部门上报类*/@ExcelProperty(value = "分类",index = 0)@NotBlank(message = "分类不能为空")private String type;@ExcelProperty(value = "评价要求",index = 5)@JsonIgnoreprivate String require;@ExcelProperty(value = "分数",index = 6)@JsonIgnoreprivate Integer score;@Builder@Data@AllArgsConstructor@NoArgsConstructorpublic static class Requirements {@ExcelIgnoreprivate String id;/*** 评价内容*/@NotBlank(message = "评价要求不能为空")private String require;/*** 分数*/@NotBlank(message = "分数不能为空")private Integer score;}
}
  1. service调用
    /*** 导入评估标准*/@Overridepublic String importEval(MultipartFile file) {EvalExcelDataListener listener = new EvalExcelDataListener(evalTemplateDao);InputStream inputStream;try {inputStream = file.getInputStream();EasyExcel.read(inputStream, EvalTemplateReq.class,listener).extraRead(CellExtraTypeEnum.MERGE).sheet().headRowNumber(3).doRead();return "全部导入成功!";} catch (IOException e) {throw new BusinessCheckException("Excel 文件流读取失败");} catch (ExcelAnalysisException e) {return e.getMessage();} catch (Exception e) {throw new BusinessException("数据导入失败", e);}}
  1. 合并单元格数据处理util
package com.gsafety.bg.pd.service.excel;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.CellExtra;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;
import java.util.List;@Slf4j
public class EasyExcelMergeUtil {/*** 处理合并单元格* @param data               解析数据* @param extraMergeInfoList 合并单元格信息* @param headRowNumber      起始行* @return 填充好的解析数据*/public static <T> List<T> explainMergeData(List<T> data, List<CellExtra> extraMergeInfoList, Integer headRowNumber) {// 循环所有合并单元格信息extraMergeInfoList.forEach(cellExtra -> {int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNumber;int lastRowIndex = cellExtra.getLastRowIndex() - headRowNumber;int firstColumnIndex = cellExtra.getFirstColumnIndex();int lastColumnIndex = cellExtra.getLastColumnIndex();// 获取初始值Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, data);// 设置值for (int i = firstRowIndex; i <= lastRowIndex; i++) {for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {setInitValueToList(initValue, i, j, data);}}});return data;}/*** 设置合并单元格的值** @param filedValue  值* @param rowIndex    行* @param columnIndex 列* @param data        解析数据*/private static <T> void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<T> data) {if (rowIndex >= data.size()) return;T 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());}}}}}/*** 获取合并单元格的初始值* rowIndex对应list的索引* columnIndex对应实体内的字段** @param firstRowIndex    起始行* @param firstColumnIndex 起始列* @param data             列数据* @return 初始值*/private static <T> Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<T> data) {Object filedValue = null;T 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;}}
  1. easyexcel数据监听类
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.metadata.CellExtra;
import com.gsafety.bg.gsdss.common.utils.json.JsonUtil;
import com.gsafety.bg.pd.dao.EvalTemplateDao;
import com.gsafety.bg.pd.model.dto.req.EvalTemplateReq;
import com.gsafety.bg.pd.model.po.EvalTemplatePO;import java.util.*;
import java.util.stream.Collectors;public class EvalExcelDataListener extends AnalysisEventListener<EvalTemplateReq> {private final Integer LIST_COUNT = 19;List<EvalTemplateReq> list = new ArrayList<>(LIST_COUNT);// 合并单元格private final List<CellExtra> extraMergeInfoList = new ArrayList<>();// 由于监听器只能通过new的方式创建,所以可以通过构造器传入dao层对象private final EvalTemplateDao dao;public EvalExcelDataListener(EvalTemplateDao dao) {this.dao = dao;}@Overridepublic void invoke(EvalTemplateReq req, AnalysisContext context) {list.add(req);if (list.size() > LIST_COUNT) {throw new ExcelAnalysisException("当前excel数据量不得大于" + LIST_COUNT + "条!");}}@Overridepublic void extra(CellExtra extra, AnalysisContext context) {if (extra.getType() == CellExtraTypeEnum.MERGE) {// 处理合并单元格的情况int firstRowIndex = extra.getFirstRowIndex();if(firstRowIndex>2) {extraMergeInfoList.add(extra);}}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {//所有数据解析完毕执行该方法// 防止导入空的Excelif (context.readRowHolder().getRowIndex() <= 0) {throw new ExcelAnalysisException("当前excel无数据!");}//处理合并单元格list = EasyExcelMergeUtil.explainMergeData(list, extraMergeInfoList, 3);saveData();}protected void saveData() {Map<String, List<EvalTemplateReq>> collect = list.stream().collect(Collectors.groupingBy(EvalTemplateReq::getItem));List<EvalTemplatePO> pos = new ArrayList<>();collect.forEach((k, v) -> {List<EvalTemplateReq.Requirements> requirements = v.stream().map(l -> EvalTemplateReq.Requirements.builder().id(v.indexOf(l) + "").require(l.getRequire()).score(l.getScore()).build()).collect(Collectors.toList());pos.add(EvalTemplatePO.builder().item(k).itemCode(collect.get(k).get(0).getItemCode()).itemExplain(collect.get(k).get(0).getItemExplain()).itemShortName(collect.get(k).get(0).getItemShortName()).type(collect.get(k).get(0).getType()).requirements(JsonUtil.of(requirements)).build());});dao.deleteAll();dao.saveAll(pos);}
}

踩坑

  1. extra方法不生效
    extraRead(CellExtraTypeEnum.MERGE)显式指定识别合并单元格数据
    invoke中原校验数据,因为为null导致拦截,不走extra方法。
    执行顺序:invoke->extra->doAfterAllAnalysed
  2. 指定表头位置后extra获取的第一个数据还是首行的合并单元格数据
    headRowNumber(3)只是指定了读取表头的位置,extra是获取整表的所有合并单元格数据,根据excel模板,要跳过2行,获取从第三行后的合并单元格数据。if(firstRowIndex>2)

参考:https://blog.csdn.net/xhmico/article/details/136905419


在这里插入图片描述

相关文章:

easyExcel 不规则模板导入数据

文章目录 前言一、需求和效果二、难点和思路三、全部代码踩坑 前言 之前分享的 EasyExcel 批量导入并校验数据&#xff0c;仅支持规则excel&#xff0c;即首行表头&#xff0c;下面对应数据&#xff0c;无合并单元格情况。 本篇主要解决问题&#xff1a; 模板excel 表头不在首…...

前端调试技巧(npm Link,vscode调试,浏览器调试等)

Npm Link 功能&#xff1a; 在本地开发npm模块的时候&#xff0c;我们可以使用npm link命令&#xff0c;将npm 模块链接到对应的运行项目中去&#xff0c;方便地对模块进行调试和测试 断点调试 vscode调试 Debug Vue2 Project 目标&#xff1a;在VSCode中调试项目代码…...

SSL证书到期自动巡检脚本-推送钉钉告警

1. 编写SSL证书巡检脚本 cat /hao/batch_check_ssl_expire.sh #!/bin/bash# 域名列表文件绝对路径 domains_file"/hao/domains.txt"#钉钉webhook webhook_url"https://oapi.dingtalk.com/robot/send?access_token99999999999999999999999999999999999999999…...

Winform打印编程基础

1、目的 进行打印设置、打印预览、及实现打印功能 2、代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Printing; using System.IO; using System.Linq; using System.Te…...

Python编程实例-Python的隐藏特性

Python的隐藏特性 文章目录 Python的隐藏特性1、Python中的下划线(_)2、通过解析树进行正则表达式调试3、省略号(...)4、dir()函数5、Lambda 函数6、链式比较运算符7、zip()函数8、修饰器9、上下文管理器和with语句10、生成器和yield语句11、元类(Metaclass)12、小结Python…...

防火墙安全策略利用

拓扑图&#xff1a; 办公区为10.0.1.0/24 生产部为&#xff1a;10.0.2.0/24 办公区为vlan2&#xff0c;生产区为vlan3 DMZ区域为10.0.3.0/24、10.0.3.10为HTTP服务器 游客区&#xff1a;210.0.0./24 ISP&#xff1a;12.0.0.0/24 要求3&#xff1a; 添加安全策略&#xf…...

SystemUIService启动-Android13

SystemUIService启动-Android13 1、SystemUIService启动2、其他SystemUI services启动2.1 Dagger依赖注入2.2 Recents为例 1、SystemUIService启动 SystemUI启动&#xff0c;及其SystemUIService启动 <!-- SystemUi service component --><string name"config_s…...

linux权限深度解析——探索原理

前言&#xff1a;本节内容主要讲述的是linux权限相关的内容&#xff0c; linux的权限如果使用root账号是感受不到的&#xff0c; 所以我们要使用普通账号对本节相关内容进行学习&#xff0c;以及一些实验的测试。 然后&#xff0c; 通过linux权限的学习我们可以知道为什么有时候…...

Qt学生管理系统(付源码)

Qt学生管理系统 一、前言1.1 项目介绍1.2 项目目标 2、需求说明2.1 功能性说明2.2 非功能性说明 三、UX设计3.1 登录界面3.2 学生数据展示3.3 信息插入和更新 三、架构说明3.1 客户端结构如下3.2 数据流程图3.2.1 数据管理3.2.2 管理员登录 四、 设计说明3.1 数据库设计3.2 结构…...

重磅!新公司法正式实施,这些变化你必须知道! ️

新公司法来了&#xff01;企业设立和经营必知的关键变动 &#x1f3db;️&#x1f680; 大家好&#xff0c;我是猫头虎&#xff0c;科技自媒体博主。今天我们来聊聊一件大事——新公司法的实施&#xff0c;这对企业设立和经营带来了哪些重大影响&#xff1f;跟着我&#xff0c…...

[Flask笔记]一个完整的Flask程序

前面讲过Flask是一个轻量级Web开发框架&#xff0c;为什么说是轻量级的呢&#xff0c;因为它用短短几行代码就能运行起来&#xff0c;我们一起来看看最简单的flask框架。 安装Flask 在看Flask框架之前我们需要先安装flask模块&#xff0c;学过python的肯定都知道&#xff0c;…...

企业专利布局怎么弄

企业专利布局策略与实施 在当今竞争激烈的商业环境中&#xff0c;企业的专利布局已成为保护创新成果、提升市场竞争力的重要手段。专利布局不仅是技术创新的体现&#xff0c;更是企业战略布局的重要一环。 一、企业专利布局的策略 多维度布局 企业专利布局应结合市场、技术、…...

ArduPilot开源飞控之AP_Mount_Topotek

ArduPilot开源飞控之AP_Mount_Topotek 1. 源由2. 框架设计3. 重要函数3.1 动态过程3.1.1 AP_Mount_Topotek::update3.1.2 AP_Mount_Backend::calculate_poi 3.2 基础能力3.2.1 AP_Mount_Topotek::healthy3.2.2 AP_Mount_Topotek::has_pan_control 3.3 设备功能3.3.1 AP_Mount_T…...

React组件间通信的几种方式

一、Props向下传递&#xff08;Top-Down Propagation&#xff09; 父组件通过props将其状态或数据传递给子组件。 父组件&#xff1a; class ParentComponent extends React.Component {state { message: Hello World };render() {return <ChildComponent message{this.…...

2024最新国际版抖音TikTok安装教程,免root免拔卡安卓+iOS,附全套安装工具!

我是阿星&#xff0c;今天给大家带来是2024年最新TikTok国际版抖音的下载和安装教程&#xff0c;而且还是免root免拔卡的那种&#xff0c;安卓和iOS都能用哦&#xff01;由于某些原因&#xff0c;国内用户并不能使用TikTok。今天阿星就教一下大家怎么安装TikTok。 TikTok在全球…...

kafka与zookeeper的SSL认证教程

作者 乐维社区&#xff08;forum.lwops.cn&#xff09;许远 在构建现代的分布式系统时&#xff0c;确保数据传输的安全性至关重要。Apache Kafka 和 Zookeeper 作为流行的分布式消息队列和协调服务&#xff0c;提供了SSL&#xff08;Secure Sockets Layer&#xff09;认证机制&…...

为何数字化转型项目会夭折?深入分析失败的关键因素

数字化转型是一个复杂的过程&#xff0c;涉及企业运营的各个方面。根据麦肯锡的报告&#xff0c;尽管数字化转型对企业至关重要&#xff0c;但根据数据显示70%的数字化转型尝试未能成功。本文总结了六大常见失败原因&#xff1a;转型准备不足、组织文化障碍、技术实施问题、人才…...

数据结构(其二)--线性表

目录 1. 基本概念 2.线性表的基本操作 3.顺序表 &#xff08;1&#xff09;.静态分配 &#xff08;2&#xff09;.动态分配 &#xff08;3&#xff09;.顺序表的插入与删除&#xff08;以静态分配为例&#xff09;&#xff08;示例代码中包含了一下必要的基本函数&#xf…...

软链接node_modules

公司项目很多微应用的子项目公用同一套模板&#xff0c;也就会使用同一个node_modules 1.先创建3个同样的项目,并安装一个其中的一个node_modules给他丢到外边 2.win r -------> cmd --------> ctrlshift enter(已管理员身份打开cmd) 3.在窗口分别执行以下代码…...

Apache中使用SSI设置

先停服务在修改httpd.conf&#xff0c;备份下 Apache\Apache24\conf 设置httpd.conf LoadModule ssl_module modules/mod_ssl.so 取消该命令前的注释符# AddType text/html .shtml AddOutputFilter INCLUDES .shtml 取消该命令前的注释符# 加入html AddType text/html .s…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...