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

easyexcel-导入(读取)(read)-示例及核心部件

文章目录

      • 导入(读取)(read)-示例及核心部件
      • 导入(读取)(read)-核心部件
          • EasyExcel(EasyExcelFactory) # 入口
          • read() # read()方法用于构建workbook(工作簿)对象,new ExcelReaderBuilder()
          • doReadAll()
          • 这里选`XlsxSaxAnalyser`这个实现类吧
          • 然后到这个类`XlsxRowHandler`,这里使用了策略模式,会根据xml不同的标签拿到对应的处理类来进行处理。
          • 从上面代码找到`RowTagHandler`这个类,每解析了一行数据后都会进行处理
          • 然后看endRow()方法
          • 进入到dealData方法中
          • 跟踪invoke()方法,选用ModelBuildEventListener这个实现类吧
          • 进入到buildUserModel方法中

这块内容有点多,单独拉出来,要不看着太乱。

导入(读取)(read)-示例及核心部件

代码:

EasyExcel.read(file.getInputStream(), UserExcelVo.class, userListener).extraRead(CellExtraTypeEnum.MERGE).sheet(0) // 指定读取哪个sheet.headRowNumber(1) // 指定标题行.doRead(); // 执行读取

导入(读取)(read)-核心部件

以上面代码为例。

EasyExcel(EasyExcelFactory) # 入口
read() # read()方法用于构建workbook(工作簿)对象,new ExcelReaderBuilder()
public class EasyExcelFactory {public static ExcelReaderBuilder read() {return new ExcelReaderBuilder();}
}
public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder<ExcelReaderBuilder, ReadWorkbook> {/*** Workbook*/private final ReadWorkbook readWorkbook;public ExcelReaderBuilder() {this.readWorkbook = new ReadWorkbook();}
}
doReadAll()
public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder<ExcelReaderBuilder, ReadWorkbook> {public void doReadAll() {try (ExcelReader excelReader = build()) {excelReader.readAll();}}
}
public class ExcelReader implements Closeable {public void readAll() {excelAnalyser.analysis(null, Boolean.TRUE);}
}
public class ExcelAnalyserImpl implements ExcelAnalyser {
@Overridepublic void analysis(List<ReadSheet> readSheetList, Boolean readAll) {try {if (!readAll && CollectionUtils.isEmpty(readSheetList)) {throw new IllegalArgumentException("Specify at least one read sheet.");}analysisContext.readWorkbookHolder().setParameterSheetDataList(readSheetList);analysisContext.readWorkbookHolder().setReadAll(readAll);try {excelReadExecutor.execute();} catch (ExcelAnalysisStopException e) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Custom stop!");}}} catch (RuntimeException e) {finish();throw e;} catch (Throwable e) {finish();throw new ExcelAnalysisException(e);}}
}
这里选XlsxSaxAnalyser这个实现类吧
public class XlsxSaxAnalyser implements ExcelReadExecutor {
@Overridepublic void execute() {for (ReadSheet readSheet : sheetList) {readSheet = SheetUtils.match(readSheet, xlsxReadContext);if (readSheet != null) {xlsxReadContext.currentSheet(readSheet);parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(xlsxReadContext));// Read commentsreadComments(readSheet);// The last sheet is readxlsxReadContext.analysisEventProcessor().endSheet(xlsxReadContext);}}}
}
然后到这个类XlsxRowHandler,这里使用了策略模式,会根据xml不同的标签拿到对应的处理类来进行处理。
public class XlsxRowHandler extends DefaultHandler {
static {CellFormulaTagHandler cellFormulaTagHandler = new CellFormulaTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_FORMULA_TAG, cellFormulaTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_FORMULA_TAG, cellFormulaTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_FORMULA_TAG, cellFormulaTagHandler);CellInlineStringValueTagHandler cellInlineStringValueTagHandler = new CellInlineStringValueTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler);CellTagHandler cellTagHandler = new CellTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_TAG, cellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_TAG, cellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_TAG, cellTagHandler);CellValueTagHandler cellValueTagHandler = new CellValueTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_VALUE_TAG, cellValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_VALUE_TAG, cellValueTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_VALUE_TAG, cellValueTagHandler);CountTagHandler countTagHandler = new CountTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.DIMENSION_TAG, countTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_DIMENSION_TAG, countTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_DIMENSION_TAG, countTagHandler);HyperlinkTagHandler hyperlinkTagHandler = new HyperlinkTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.HYPERLINK_TAG, hyperlinkTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_HYPERLINK_TAG, hyperlinkTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_HYPERLINK_TAG, hyperlinkTagHandler);MergeCellTagHandler mergeCellTagHandler = new MergeCellTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.MERGE_CELL_TAG, mergeCellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_MERGE_CELL_TAG, mergeCellTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_MERGE_CELL_TAG, mergeCellTagHandler);RowTagHandler rowTagHandler = new RowTagHandler();XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.ROW_TAG, rowTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_ROW_TAG, rowTagHandler);XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_ROW_TAG, rowTagHandler);}
}
从上面代码找到RowTagHandler这个类,每解析了一行数据后都会进行处理
public class RowTagHandler extends AbstractXlsxTagHandler {
@Overridepublic void endElement(XlsxReadContext xlsxReadContext, String name) {XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder();RowTypeEnum rowType = MapUtils.isEmpty(xlsxReadSheetHolder.getCellMap()) ? RowTypeEnum.EMPTY : RowTypeEnum.DATA;// It's possible that all of the cells in the row are emptyif (rowType == RowTypeEnum.DATA) {boolean hasData = false;for (Cell cell : xlsxReadSheetHolder.getCellMap().values()) {if (!(cell instanceof ReadCellData)) {hasData = true;break;}ReadCellData<?> readCellData = (ReadCellData<?>)cell;if (readCellData.getType() != CellDataTypeEnum.EMPTY) {hasData = true;break;}}if (!hasData) {rowType = RowTypeEnum.EMPTY;}}xlsxReadContext.readRowHolder(new ReadRowHolder(xlsxReadSheetHolder.getRowIndex(), rowType,xlsxReadSheetHolder.getGlobalConfiguration(), xlsxReadSheetHolder.getCellMap()));xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext);xlsxReadSheetHolder.setColumnIndex(null);xlsxReadSheetHolder.setCellMap(new LinkedHashMap<>());}
}
然后看endRow()方法
public class DefaultAnalysisEventProcessor implements AnalysisEventProcessor {@Overridepublic void endRow(AnalysisContext analysisContext) {if (RowTypeEnum.EMPTY.equals(analysisContext.readRowHolder().getRowType())) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Empty row!");}if (analysisContext.readWorkbookHolder().getIgnoreEmptyRow()) {return;}}dealData(analysisContext);}
}
进入到dealData方法中
private void dealData(AnalysisContext analysisContext) {ReadRowHolder readRowHolder = analysisContext.readRowHolder();Map<Integer, ReadCellData<?>> cellDataMap = (Map)readRowHolder.getCellMap();readRowHolder.setCurrentRowAnalysisResult(cellDataMap);int rowIndex = readRowHolder.getRowIndex();int currentHeadRowNumber = analysisContext.readSheetHolder().getHeadRowNumber();boolean isData = rowIndex >= currentHeadRowNumber;// Last head columnif (!isData && currentHeadRowNumber == rowIndex + 1) {buildHead(analysisContext, cellDataMap);}// Now is datafor (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {try {if (isData) {readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext);} else {readListener.invokeHead(cellDataMap, analysisContext);}} catch (Exception e) {onException(analysisContext, e);break;}if (!readListener.hasNext(analysisContext)) {throw new ExcelAnalysisStopException();}}}

这里可以看到,每解析了一行数据后都会逐个调用监听器的invoke方法(包括easyexcel默认的监听器和我们自定义的监听器)

跟踪invoke()方法,选用ModelBuildEventListener这个实现类吧

进入到ModelBuildEventListener实现类,这个实现类是easyexcel自带的默认的监听器,这个监听器的主要作用是将excel的每行数据封装

public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<Integer, ReadCellData<?>>> {@Overridepublic void invoke(Map<Integer, ReadCellData<?>> cellDataMap, AnalysisContext context) {ReadSheetHolder readSheetHolder = context.readSheetHolder();if (HeadKindEnum.CLASS.equals(readSheetHolder.excelReadHeadProperty().getHeadKind())) {context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(cellDataMap, readSheetHolder, context));return;}context.readRowHolder().setCurrentRowAnalysisResult(buildNoModel(cellDataMap, readSheetHolder, context));}
}
进入到buildUserModel方法中
private Object buildUserModel(Map<Integer, ReadCellData<?>> cellDataMap, ReadSheetHolder readSheetHolder,AnalysisContext context) {ExcelReadHeadProperty excelReadHeadProperty = readSheetHolder.excelReadHeadProperty();Object resultModel;try {resultModel = excelReadHeadProperty.getHeadClazz().newInstance();} catch (Exception e) {throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0,new ReadCellData<>(CellDataTypeEnum.EMPTY), null,"Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);}Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();BeanMap dataMap = BeanMapUtils.create(resultModel);for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {Integer index = entry.getKey();Head head = entry.getValue();String fieldName = head.getFieldName();if (!cellDataMap.containsKey(index)) {continue;}ReadCellData<?> cellData = cellDataMap.get(index);Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(),ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(),fieldName, readSheetHolder), readSheetHolder.converterMap(), context,context.readRowHolder().getRowIndex(), index);if (value != null) {dataMap.put(fieldName, value);}}return resultModel;}

可以看到,先是通过反射创建对象,然后根据反射对各个对象赋值。

至此,excel的一行数据就被easyexcel默认的监听器解析成了对应的java对象,然后我们在自定义的监听器中就能拿到这个对象并进行处理了。

相关文章:

easyexcel-导入(读取)(read)-示例及核心部件

文章目录 导入(读取)(read)-示例及核心部件导入(读取)(read)-核心部件EasyExcel(EasyExcelFactory) # 入口read() # read()方法用于构建workbook(工作簿)对象&#xff0c;new ExcelReaderBuilder()doReadAll()这里选XlsxSaxAnalyser这个实现类吧然后到这个类XlsxRowHandler&…...

IPhone13 Pro Max设备详情

目录 产品宣传图内部图——后设备详细信息 产品宣传图 内部图——后 设备详细信息 信息收集于HubWeb.cn...

K8S中高级存储之PV和PVC

高级存储 PV和PVC 由于kubernetes支持的存储系统有很多&#xff0c;要求客户全都掌握&#xff0c;显然不现实。为了能够屏蔽底层存储实现的细节&#xff0c;方便用户使用&#xff0c; kubernetes引入PV和PVC两种资源对象。 PV&#xff08;Persistent Volume&#xff09; PV是…...

SVG 矩形:深入理解与实际应用

SVG 矩形&#xff1a;深入理解与实际应用 引言 SVG&#xff08;可缩放矢量图形&#xff09;是一种基于可扩展标记语言的图形矢量格式&#xff0c;用于在网页上创建矢量图形。SVG矩形是SVG图形中的一种基本形状&#xff0c;它允许用户在网页上绘制不同大小和颜色的矩形。本文将…...

高效学习方法分享

高效学习方法分享 引言 在信息高速发展的今天&#xff0c;学习已经成为每个人不可或缺的一部分。你是否曾感到学习的疲惫&#xff0c;信息的爆炸让你无从下手&#xff1f;今天&#xff0c;我们将探讨几种高效的学习方法&#xff0c;帮助你从中找到适合自己的学习之道。关于学…...

Linux---架构概览

一、Linux 架构分层的深度解析 1. 用户空间&#xff08;User Space&#xff09; 用户空间是应用程序运行的环境&#xff0c;与内核空间隔离&#xff0c;确保系统稳定性。 应用程序层&#xff1a; 用户程序&#xff1a;如 edge、vim&#xff0c;通过调用标准库&#xff08;如 …...

2501,20个窗口常用操作

窗口是屏幕上的一个矩形区域.窗口分为3种:覆盖窗口,弹窗和子窗口.每个窗口都有由系统绘画的"非客户区"和应用绘画的"客户区". 在MFC中,CWnd类为各种窗口提供了基类. 1,通过窗柄取得CWnd指针 可调用Cwnd::FromHandle函数,通过窗柄取得Cwnd指针. void CD…...

[论文总结] 深度学习在农业领域应用论文笔记14

当下&#xff0c;深度学习在农业领域的研究热度持续攀升&#xff0c;相关论文发表量呈现出迅猛增长的态势。但繁荣背后&#xff0c;质量却不尽人意。相当一部分论文内容空洞无物&#xff0c;缺乏能够落地转化的实际价值&#xff0c;“凑数” 的痕迹十分明显。在农业信息化领域的…...

蓝桥杯例题一

不管遇到多大的困难&#xff0c;我们都要坚持下去。每一次挫折都是我们成长的机会&#xff0c;每一次失败都是我们前进的动力。路漫漫其修远兮&#xff0c;吾将上下而求索。只有不断努力奋斗&#xff0c;才能追逐到自己的梦想。不要害怕失败&#xff0c;害怕的是不敢去尝试。只…...

WPF基础 | 深入 WPF 事件机制:路由事件与自定义事件处理

WPF基础 | 深入 WPF 事件机制&#xff1a;路由事件与自定义事件处理 一、前言二、WPF 事件基础概念2.1 事件的定义与本质2.2 常见的 WPF 事件类型 三、路由事件3.1 路由事件的概念与原理3.2 路由事件的三个阶段3.3 路由事件的标识与注册3.4 常见的路由事件示例 四、自定义事件处…...

C++封装红黑树实现mymap和myset和模拟实现详解

文章目录 map和set的封装map和set的底层 map和set的模拟实现insertiterator实现的思路operatoroperator- -operator[ ] map和set的封装 介绍map和set的底层实现 map和set的底层 一份模版实例化出key的rb_tree和pair<k,v>的rb_tree rb_tree的Key和Value不是我们之前传统意…...

洛谷P11464 支配剧场

支配剧场 题目背景 May all the beauty be blessed. 题目描述 布洛妮娅和符华在寻找琪亚娜的途中&#xff0c;被支配之律者困在了支配剧场的高塔回廊之中。布洛妮娅敏锐地发现&#xff0c;虚无回廊是由一些支配之律者生成的积木构成的&#xff0c;只要击碎其中一些积木&#…...

自动化数据备份与恢复:让数据安全无忧

友友们好! 我的新专栏《Python进阶》正式启动啦!这是一个专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会找到: ● 深入解析:每一篇文章都将…...

如何用matlab画一条蛇

文章目录 源代码运行结果代码说明结果 源代码 % 画蛇的代码 % 2025-01-28/Ver1 % 清空环境 clc; clear; close all;% 定义蛇的身体坐标 t linspace(0, 4*pi, 100); % 参数化变量 x t; % x坐标 y sin(t) 0.5 * sin(3*t); % y坐标&#xff0c;形成更复…...

DVC - 数据版本和机器学习实验的命令行工具和 VS Code 扩展

文章目录 一、关于 DVC二、快速启动三、DVC的工作原理四、VS代码扩展五、安装Snapcraft&#xff08;Linux&#xff09;Chocolatey (Windows)Brew (mac OS)Anaconda (Any platform)PyPI&#xff08;Python&#xff09;Package (Platform-specific)Ubuntu / Debian (deb)Fedora /…...

理解神经网络:Brain.js 背后的核心思想

温馨提示 这篇文章篇幅较长,主要是为后续内容做铺垫和说明。如果你觉得文字太多,可以: 先收藏,等后面文章遇到不懂的地方再回来查阅。直接跳读,重点关注加粗或高亮的部分。放心,这种“文字轰炸”不会常有的,哈哈~ 感谢你的耐心阅读!😊 欢迎来到 brain.js 的学习之旅!…...

Maui学习笔记- SQLite简单使用案例02添加详情页

我们继续上一个案例&#xff0c;实现一个可以修改当前用户信息功能。 当用户点击某个信息时&#xff0c;跳转到信息详情页&#xff0c;然后可以点击编辑按钮导航到编辑页面。 创建项目 我们首先在ViewModels目录下创建UserDetailViewModel。 实现从详情信息页面导航到编辑页面…...

typescript 简介

可选链操作符 可选链操作符( ?. ) 允许读取位于连接对象链深处的属性的值&#xff0c;而不必明确验证链中的每个引用是否有效。?. 操作符的功能类似于 . 链式操作符&#xff0c;不同之处在于&#xff0c;在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误&a…...

selenium定位网页元素

1、概述 在使用 Selenium 进行自动化测试时&#xff0c;定位网页元素是核心功能之一。Selenium 提供了多种定位方法&#xff0c;每种方法都有其适用场景和特点。以下是通过 id、linkText、partialLinkText、name、tagName、xpath、className 和 cssSelector 定位元素的…...

Autogen_core 测试代码:test_cache_store.py

目录 原始代码测试代码代码中用到的typing注解 原始代码 from typing import Dict, Generic, Optional, Protocol, TypeVarT TypeVar("T")class CacheStore(Protocol, Generic[T]):"""This protocol defines the basic interface for store/cache o…...

变压器的漏感

测量变压器漏感的时候需要将次级绕组短路&#xff1a; 测量变压器初级线圈的电感方法很简单&#xff0c;直接用LCR测量就可&#xff0c;无需像测量漏感那样将次级绕组短接&#xff1a;...

【新春特辑】2025年春节技术展望:蛇年里的科技创新与趋势预测

&#x1f525;【新春特辑】2025年春节技术展望&#xff1a;蛇年里的科技创新与趋势预测 &#x1f4c5; 发布日期&#xff1a;2025年01月29日&#xff08;大年初一&#xff09; 在这个辞旧迎新的美好时刻&#xff0c;我们迎来了充满希望的2025年&#xff0c;也是十二生肖中的蛇…...

cursor软件的chat和composer分别是什么

Cursor 是一款基于人工智能的代码编辑器&#xff0c;集成了类似 ChatGPT 的功能&#xff0c;旨在帮助开发者更高效地编写代码。以下是 Cursor 中 Chat 和 Composer 的具体功能&#xff1a; 1. Chat Cursor 中的 Chat 是一个基于 AI 的聊天功能&#xff0c;类似于 ChatGPT&…...

从ChatGPT热潮看智算崛起

2025年1月7日&#xff0c;科智咨询发布《2025年IDC产业七大发展趋势》&#xff0c;其中提到“ChatGPT开启生成式AI热潮&#xff0c;智能算力需求暴涨&#xff0c;算力供给结构发生转变”。 【图片来源于网络&#xff0c;侵删】 为何会以ChatGPT发布为节点呢&#xff1f;咱们一起…...

攻克 AI 幻觉难题

当下&#xff0c;AI 已经成为我们生活中不可或缺的一部分。无论是智能语音助手&#xff0c;还是对话式的AI模型&#xff0c;它们凭借强大的算法和海量的数据&#xff0c;为我们答疑解惑、出谋划策。 然而&#xff0c;小编今天向AI提问&#xff1a;上山打老虎。他却回答&#x…...

格式化时间的插件

1.安装dayjs包 npm i dayjs 2.组件中的应用...

自创《艺术人生》浅析

艺术是生活的馈赠&#xff0c;艺术是苦痛的呻吟。 笔记模板由python脚本于2025-01-29 00:01:11创建&#xff0c;本篇笔记适合喜欢写诗读诗诵诗的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 …...

【Python-办公自动化】实现自动化输出json数据类型的分析报告和正逆转换

分析报告 import json from pprint import pprint, PrettyPrinterdef analyze_energy_data(file_path):"""能源数据分析与结构查看函数参数:file_path (str): JSON文件路径功能:1. 加载并解析JSON数据2. 显示数据结构概览3. 交互式结构探索"""…...

防御保护第一次实验:安全策略配置

一、实验拓扑 二、实验要求 三、需求分析 1.创建两个vlan 2.在ENSP中配置基于时间的ACL实现对于办公区PC访问OA Server的时间限制&#xff08;工作日早8到晚6&#xff09;。 3.通过配置基于MAC地址的ACL来实现对于生产区PC访问Web Server的限制&#xff08;除PC3外不能访问&am…...

【Pytest】生成html报告中,中文乱码问题解决方案

链接上一篇文章:https://blog.csdn.net/u013080870/article/details/145369926?spm1001.2014.3001.5502 中文乱码问题&#xff0c;python3&#xff0c;Python3.7后&#xff0c;还一个文件就是result.py 因为中文可以在内容中&#xff0c;也可能在文件名&#xff0c;类名&…...