easy-excel 读取每一行为List<String>

通常代码如下所示

public class WaterLevelImportListener extends AnalysisEventListener<ExcelMode> {
 @Override
  public void invoke(ExcelMode data, AnalysisContext context) {

继承AnalysisEventListener重写invoke方法,在invoke方法的第一参数是实题类的名称,每从excel中读取一行数据都会封装到这个参数中
我的问题是
我不想数据封装成实体类,而是List<String>改如何做?

阅读 11.6k
2 个回答

可以把你指定监听器的泛型实体类 ExcelMode 改成一个Map来接收,excel文件会被映射成:key 为列名(0、1、2…) value 值为单元格内容。

然后再做一个 Map的遍历 转换成 List 就可以。

以上。

首先承认我其实之前没用过easy-excel哈,不过看到是阿里出的,所以就下载下来试了一下(用的最新版本2.2.3)。先说下我自己看下来的结论吧:

可以支持List<String>,但是其实官方现在方向还是希望写实体类,个人建议也不要写List<String>

什么意思呢?我查看了源码,把xls中每一行的数据转换为对应的实体类,主要是在这个类ModelBuildEventListener中的invoke方法
image.png

而这个方法执行的时候,首先会有一个if判断,判断HeadKindEnum的类型,而我们一般按照下面的方式调用的话

ExcelReader excelReader = EasyExcel.read(fileName, ExcelMode.class, listener).build()

那这个HeadKindEnum就是CLASS

image.png

然后执行buildUserModel方法返回对应的实体对象,这个对象也就是后面传入listener里的实体对象

而我们稍加注意的话,会发现,假设这个if判断没有走进去,它的另一种走法,不再走buildUserModel而是走

buildStringList

你看看这个名字,是不是很心动,对,当时我也很心动。但我没有急着先去看具体实现,因为要走到这个方法,必须绕开这个if判断,我再次走读了一下源码,发现这里的HeadKindEnum是根据你有没有传入实体对象的class来的,具体逻辑在ExcelHeadProperty.initColumnProperties方法
image.png

而这个ExcelHeadPropert又是根据你最开始调用的EasyExcel.read方法中第二个参数来的,所以马上查看一下EasyExcel其他的read重载方法,是有两个参数的,也就是不带实体class
image.png

因此这下可以去查看一下buildStringList的具体实现了,走进这个方法可以看到(有点长,不好截图,我就简单把代码逻辑下来)

private Object buildStringList(Map<Integer, CellData> cellDataMap, ReadHolder currentReadHolder,
        AnalysisContext context) {
   int index = 0;
   if (context.readWorkbookHolder().getDefaultReturnMap()) {
       Map<Integer, String> map = new LinkedHashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);   
       // 逻辑处理
       return map;
  }else {
       List<String> list = new ArrayList<String>();
       // 逻辑处理
       return list;
  }
}

简单的可以看到,虽然方法名叫buildStringList,但是实际上还是两种返回可能

  1. Map<Integer, String>
  2. List<String>

而通过源码的一段注释可以看到,其实在老版本里最早应该是只返回List的,返回map是后面改的,保留返回List只是为了兼容老版本

image.png

找了一下提交记录,可以看到这一次的提交

image.png

就是把之前默认返回list改为返回返回map,当然后面这可能是出现了不兼容,所以才改为根据一个配置来兼容,也就是这里的一个if判断

image.png

判断的是ReadWorkbookHolder里的defaultReturnMap属性,我们进入源码看到这个属性已经@Deprecated了,也就是说这个属性以后会被删掉的,现在也是不鼓励使用,后续不会再维护的属性
image.png

当然我已经使用了现在的最新版本,但是这个最新版本这个属性还没有删掉,所以这就是我最早说的结论,我们应该可以改变这个属性,但是由于版本的趋势来说,还是不建议返回List,因为早晚可能会被删掉

所以如果你想返回Map的话,也就是向楼上那个兄弟说的,你改成Map,然后调用的时候使用EasyExcel.read的两参方法即可,这个我试过可以的
image.png

当然如果你想看如何返回List,那稍微有点麻烦。我是这么思考的,我们要给defaultReturnMap赋值才行

搜了一下这个属性被赋值的地方,唯一有被调用的地方是ReadWorkbookHolder的带参数ReadWorkbook构造方法
image.png

defaultReturnMap的值又是根据ReadWorkbookdefaultReturnMap属性赋值的,这个属性走到源码一看,也是被@Deprecated,再搜索一下ReadWorkbookdefaultReturnMap被赋值的地方,虽然有一个setDefaultReturnMap方法,但是该方法被ExcelReader的构造方法调用,而这个方法也被@Deprecated
image.png

如果题主你以前用过easy-excel,那你应该明白到这里应该怎么去调用了吧,因为这是老方法的调用模式,而我之前没用过easy-excel,所以虽然ExcelReader的构造方法没有被删除,但是我没有朝这个思路去想了

因为虽然老方法被删除,现在版本推荐采用EasyExcel.read的方式创建,但是他们只是创建的起手式不一样,归根到底他们都需要去创建一个ReadWorkbook,也就是说它们都是为创建ReadWorkbook服务的

EasyExcel.read的实现可以看到,它是通过ExcelReaderBuilder来创建ReadWorkbook
image.png

ExcelReaderBuilder也只是简单把ReadWorkbook包装起来,一个包装器,然后暴露一些ReadWorkbook的方法而已
image.png

当然它没有暴露ReadWorkbooksetDefaultReturnMap方法了

所以我的思路就很简单了,既然原版的服务创建ReadWorkbook的方式不满足我,那我就自己来一套

没有什么是ctrl+c,v解决不了的,于是我的建议方法就是,把ExcelReaderBuilder直接所有copy一份,改一个名字CustomExcelReaderBuilder,这样我们就可以自己在这个builder里新增一个类似setDefaultReturnMap方法,就如
image.png

当然由于不用ExcelReaderBuilder了,那EasyExcel.read也不能用了,很简单还是自己造,还是可以copy它的所有方法,如果你嫌弃麻烦,那就只copy你用到的方法

image.png

当然注意,我们调用的是两参的方法,因此在三参的方法中加入setDefaultReturnMap的位置要加对地方哈

最后在执行一把

image.png

问题不大,可以达到效果哈,虽然不知不觉都这个点了,有点晚了,这种找问题的感觉就是很过瘾哈,不过多谢招待,简单学了一把easy-excel,告辞!

(最后还是强烈建议不要用List,你总不希望之后的人看到你这个代码,要骂你这个List里到底装了什么业务类型的字段吧Σ(っ °Д °;)っ)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题