【Java使用EasyExcel合并单元格】处理合并的单元格并获取到实体类

在开发中遇到了一个第一列需要合并单元格 , 然后和后面的列形成父子关系 , 并需要返回一个树形结构的实体类的需求 , 可以使用AnalysisEventListener回调监听器实现

一. AnalysisEventListener简介

在 Java 里,AnalysisEventListener 是阿里巴巴开源项目 EasyExcel 中的一个关键类,它主要用于在解析 Excel 文件时处理数据。

1. 所属库

AnalysisEventListener 属于 EasyExcel 库,该库是一个基于 Java 的简单、省内存的读写 Excel 工具,在处理大数据量的 Excel 文件时优势显著。

2. 类的作用

AnalysisEventListener 是一个抽象类,你可以通过继承它来实现自定义的监听器,在 Excel 文件解析过程中对数据进行处理。当 EasyExcel 逐行解析 Excel 文件时,会触发监听器中的相应方法,让你能够对每一行数据进行处理。

3. 常用方法及其用途

invoke(T data, AnalysisContext context)
  • 功能:每当解析到一行数据时,此方法就会被调用。data 是当前行解析后的数据对象,context 包含了解析过程中的上下文信息。
  • 示例
@Override
public void invoke(MyData data, AnalysisContext context) {
    // 处理每一行数据
    System.out.println("解析到一行数据:" + data);
}
doAfterAllAnalysed(AnalysisContext context)
  • 功能:当整个 Excel 文件解析完毕后,该方法会被调用。你可以在这个方法中进行一些收尾工作,比如保存数据到数据库、输出统计信息等。
  • 示例
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
    // 所有数据解析完成,进行收尾工作
    System.out.println("Excel 文件解析完成");
}
extra(CellExtra extra, AnalysisContext context)
  • 功能:当解析到一些额外信息(如合并单元格、超链接、批注等)时,此方法会被调用。extra 包含了额外信息的具体内容。
  • 示例
@Override
public void extra(CellExtra extra, AnalysisContext context) {
    if (extra.getType() == CellExtraTypeEnum.MERGE) {
        // 处理合并单元格信息
        System.out.println("发现合并单元格:" + extra);
    }
}

4. 使用步骤

步骤 1:引入依赖

在 Maven 项目中,你需要在 pom.xml 文件里添加 EasyExcel 的依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.1</version>
</dependency>
步骤 2:创建数据类

创建一个 Java 类来映射 Excel 文件中的数据:

@Data
public class ParentChiled{
    private String name;
    private Integer sex;

}
步骤 3:创建监听器

继承 AnalysisEventListener 类,实现自定义监听器:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

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

public class MyDataListener extends AnalysisEventListener<ParentChiled> {
    private List<ParentChiled> dataList = new ArrayList<>();

    @Override
    public void invoke(MyData data, AnalysisContext context) {
        dataList.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 处理所有数据
        System.out.println("共解析到 " + dataList.size() + " 条数据");
    }

    public List<MyData> getDataList() {
        return dataList;
    }
}

二. 处理合并单元格的实例

1.接收Excel文件,在ServiceImpl层进行处理

@Override
public Map<String, Object> readExcel(MultipartFile file) {
    try(InputStream inputStream = file.getInputStream()) {

        MergedExcelListener listener = new MergedExcelListener();
        EasyExcel.read(inputStream)
                .extraRead(CellExtraTypeEnum.MERGE)// 启用合并单元格解析
                .registerReadListener(listener)
                .sheet()
                .headRowNumber(2) // 跳过表头
                .doRead();
        List<Parent> result = listener.getResult();

        return ReturnMapUtil.getSuccessMap(result);
    }catch (Exception e) {
        e.printStackTrace();
        return ReturnMapUtil.getErrorMapWithMessage(e.getMessage());
    }
}
    
2. 继承AnalysisEventListener 处理Excel
public class MergedExcelListener extends AnalysisEventListener<Map<Integer, String>> {
    private List<Parent> result = new ArrayList<>();
    private List<CellExtra> mergedRegions = new ArrayList<>();
    private List<Map<Integer, String>> rawDataRows = new ArrayList<>(); // 存储原始数据行
    private List<Integer> rowIndexes = new ArrayList<>(); // 存储每行的索引

    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        if (extra.getType() == CellExtraTypeEnum.MERGE) {
            mergedRegions.add(extra);
        }
    }

    @Override
    public void invoke(Map<Integer, String> dataMap, AnalysisContext context) {
        // 暂存原始数据和行号
        rawDataRows.add(dataMap);
        rowIndexes.add(context.readRowHolder().getRowIndex());
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 所有数据收集完毕后,统一处理
        processMergedData();
    }

    private void processMergedData() {
        Parent currentParent = null;
        // 遍历每一行原始数据
        for (int i = 0; i < rawDataRows.size(); i++) {
            Map<Integer, String> dataMap = rawDataRows.get(i);
            int rowIndex = rowIndexes.get(i);
            int mergedColumn = 0; // 合并列索引

            // 判断是否为合并起始行
            boolean isMergeStart = isMergeStartRow(rowIndex, mergedColumn);
            String firstColumnValue = dataMap.get(mergedColumn);

            // 如果是合并起始行或者第一列有新值,创建新的父对象
            if (isMergeStart || (firstColumnValue != null && !firstColumnValue.isEmpty() && (currentParent == null ||!firstColumnValue.equals(currentParent.getJcxm())))) {
                currentParent = new Parent();
                currentParent.setParentName(firstColumnValue);
                currentParent.setChiledList(new ArrayList<>());
                result.add(currentParent);
            }

            // 将当前行数据添加到最近的父对象
            if (currentParent != null) {
                ParentChiled child = new ParentChiled();
                child.setName(dataMap.get(1)); // 子列1
                child.setSex(dataMap.get(2)); // 子列2
                currentParent.getChiledList().add(child);
            }
        }
    }

    // 判断当前行是否为某个合并区域的起始行
    private boolean isMergeStartRow(int row, int column) {
        for (CellExtra merge : mergedRegions) {
            if (merge.getFirstRowIndex() == row
                    && merge.getFirstColumnIndex() <= column
                    && merge.getLastColumnIndex() >= column) {
                return true;
            }
        }
        return false;
    }

    public List<Parent> getResult() {
        return result;
    }
}
3. 使用

在这里插入图片描述
比如说这种格式的Excel导入就会获得

[
  {
    "parentName": "组长",
    "chiledList": [
      {
        "name": "张三",
        "sex": "男"
      },
      {
        "name": "李四",
        "sex": "女"
      }
    ]
  },
  {
    "parentName": "总经理",
    "chiledList": [
      {
        "name": "赵六",
        "sex": "男"
      }
    ]
  }
]
### 使用 EasyExcel 实现合并单元 #### 自定义监听器收集合并单元信息 为了实现读取含有合并单元Excel 文件,在监听器中添加了用于收集合并单元的相关代码[^1]。 ```java public class MergeCellListener extends AnalysisEventListener<YourDataClass> { private List<CellRangeAddress> mergeRegions = new ArrayList<>(); @Override public void invoke(YourDataClass data, AnalysisContext context) { // 收集需要合并的信息逻辑... } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 所有数据解析完成后的处理... } } ``` #### 基于规则配置实现简单的单元格合并效果 通过设定一些基本规则来决定哪些单元格应该被合并,可以达到预期的效果展示。此过程涉及到了对特定区域内的值进行比较以及设置相应的样式属性[^2]。 #### 动态依据单元格数值判定执行合并操作 当向 Excel 表单内写入数据时,可以根据实际需求动态地判断相邻两行或多行间是否存在相同的内容;如果满足预设条件,则对该部分实施跨行列方向上的联合显示动作[^3]。 ```java // 获取到工作簿对象之后立即注册处理器实例 writeWorkbook.registerWriteHandler(new CellMergeStrategy(dataList)); ``` #### 利用注解与 RowWriteHandler 完成高效定制化方案 采用 `@Column` 注解标记实体类字段对应表列的同时,配合实现了接口 `RowWriteHandler` 的组件来进行更灵活精准控制下的批量合并不再困难重重[^4]。 ```java // 创建自定义合并策略类继承 AbstractRowWriteHandlerAdapter 重写相应方法 public class CustomMergeStrategy extends AbstractRowWriteHandlerAdapter { @Override protected void afterSheetCreate(Sheet sheet) {} @Override protected void afterRowDispose(Table table, int relativeRowIndex, boolean isHead) {} } // 调用API创建文件流、指定模板映射关系后应用上述策略最终写出目标文档 EasyExcel.write(filePath).withTemplate(templatePath) .registerWriteHandler(new CustomMergeStrategy()) .sheet() .doFill(dataMap); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值