easyexcel reads excel merged cell data

    There is no problem with easyexcel reading ordinary excel lists. However, if there is a merged cell, when it reads, it can get the data, but the data is incomplete. Cell data as shown below:

    We read through a simple asynchronous, and finally view the data content:

  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());
    }
}

    Run the program and print the log information as follows:

   It is true that 6 data were obtained, but there is a data acquisition in each merged cell record that is empty.

    The solution is to read the extra data of the merged cell in the asynchronous read data listener and supplement this part of the data.

    What needs to be modified:

    1. The entity needs to add annotation index value:

@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. Customize the listener to read the merged cell data:

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. Read the data through the listener, get the data and merge the cell data through the listener, and then set the cell data.


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;
    }
}

    There is a small detail to note. When we read through the listener, we need to additionally read the merged cells.

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

    Another small detail is that our tables generally have a header. The content of the header may be 2 rows or 1 row. When merging cells, you need to consider this number of rows. We defined a constant HEAD_ROW_NUM to record the number of rows, and passed it in when we finally calculated the cell value. 

    When processing excel data, I found that if the cell merging is done by our program itself, then there will be no problem when it is read. Wherever it is null above, it actually has a value:

    But in practice, our excel cannot guarantee that it will not be edited manually, so it is very likely that when we merge cells, we will merge the values ​​and the non-values ​​together, and finally the read merge mentioned above will occur. There is a problem with missing data in the cells. And we expect that the data in the merged cells should be the same. So today's solution is a safe approach. Regardless of whether your table has merged cells or whether it has been manually modified, you can eventually recover the missing data in the merged cells.

Guess you like

Origin blog.csdn.net/feinifi/article/details/130437926
Recommended