使用模板将数据实体渲染为Excel或使用模板将Excel提取为数据实体的组件:template-excel
近期,由于需要对项目中的数据进行治理,设计到大量Excel文件的读取。为了简化工作,开发了template-excel组件。本项目已在gitee上开源,源码地址:https://gitee.com/beiding/template-excel。demo地址:https://gitee.com/beiding/template-excel-demo
安装及使用:
1 添加maven依赖:
<dependency>
<groupId>com.gitee.beiding</groupId>
<artifactId>template-excel</artifactId>
<version>1.2.0-RELEASE</version>
</dependency>
2 渲染操作:
2.1 编写渲染模板:
渲染表达式请翻阅下文
2.2 读取渲染模板并对数据实体进行渲染
//将需要渲染的数据放入Data容器中 data = Data.create(); data.put("students", students); //模板Excel的输入流 templateInputStream = new FileInputStream("模板.xlsx"); //渲染结果的输出流 resultOutputStream = new FileOutputStream("结果.xlsx"); Renderer.render(data, templateInputStream, resultOutputStream);
渲染后生成的Excel:
3 提取操作:
3.1 编写提取模板:
//你也可以在Excel文件中编写提取模板,然后使用流的方式读取 XSSFSheet templateSeet = FastSheet.create("Sheet1") .newRow() .cell("A","序号").cell("B", "姓名").cell("C", "科目编号").cell("D", "科目") .newRow() .cell("A","${/[0-9]*/}").cell("B", "${students[i].name/张伟[0-9]+/}").cell("C", "${students[i].indexs[j]}").cell("D", "选修科目:${students[i].subjects[j]}") .toXSSFSheet();
提取表达式编写语法请翻阅下文
3.2 提取数据Excel中的数据
ExtractResult result = Extractor.extract( templateSeet //提取模板 , dataInputStream//数据excel输入流 , TypeMapperBuilder.createAndMapping("students[i]", Student.class).toMap(), ColNumberMatchingMode.EQUALS);//实体映射 //从提取的结果中获取数据实体 List<Student> list = result.get("students", List.class); for (Student student : list) { System.out.println(student); }
渲染表达式说明 1.${xxx}是渲染表达式,在渲染表达式之外的文本将会原样输出. 2.如果使用"[index]"作为某个实体的后缀,则表示这个实体是可遍历的,渲染引擎会将其遍历展开 例如:表达式"${students[i].name}"中的students实体会被遍历展开,并且将展开的每个实体的name属性填入该单元格 3.在同一行中对使用了同名自增变量的多个渲染表达式,其遍历过程是同步的 例如:表达式"${students[i].name}"和表达式"${students[i].indexs[j]}"都使用了i作为students的自增变量,因此在这两个单元格展开时使用的是同一个Student实体 4.渲染表达式支持多维数组或多层级数组的展开 例如:array[i][j]或者students[i].indexs[j] 多维数组或多层级数组展开时,引擎将采用字典顺序确定自增变量的层级关系 例如:表达式"${students[i].indexs[j]}"中存在i和j两个自增变量,那么在展开时自增变量将采用以下等效的层级关系 for(i=0;i<iMax;i++){ for(j=0;j<jMax;j++){ ... } } *确定自增变量的层级关系有时也是一件重要的事情,因为这会影响单元格合并情况 5.当同一行中渲染表达式渲染的结果存在一对多关系时,引擎会自动将一的那一方进行单元格合并 例如:表达式"${students[i].name}"和表达式"${students[i].indexs[j]}" Student实体中name和indexs之间是存在一对多的关系的,因此name会自动单元格合并 6.可以采用函数对数据进行操作后再进行遍历输出: 例如:表达式"${reversal(students[i].subjects)[j]}"中,reversal就是一个反转函数,该表达式能够叫Student实体中的subjects列表反转后再进行遍历输出(无需担心性能问题,因为reversal函数是带有缓存的. 你也可以使用com.beiding.template-excel.Js.defineFunction函数自定义函数,使用该方法定义的函数都会自带缓存,请不要在方法定义中手动调用_cacheGet和_cachePut,可能会造成无限递归问题) 指令函数说明 1. 由于无法在数据展开的过程中确定数据行的索引,因此对于依赖数据行索引生成的数据就无法使用渲染表达式生成,指令函数提供一种方式在数据填充进单元格时生成所需的数据 2. 格式为"$xxx(xx)",例如"$index(from)" 3. 你可以使用com.beiding.template-excel.Js.defineCommand定义一个指令函数. 例如: Js.defineCommand("function $test(){return "测试"}") 指令定义中,你可以引用"_index",这是一个以模板行为单位的索引引用,从0开始自增模板行展开的行数 例如:一个模板行展开了20行,index会从0开始自增到19 你也可以引用_globalIndex,这是从0开始的当前Sheet页的全局行号引用 _col是当前列的列号,从0开始 *注意:指令函数不要写在渲染表达式中 提取表达式说明 1. ${xxx/regex/}是提取表达式的标准格式,xxx是接收提取结果的实体字段,regex是匹配正则 例如:${student.name/张伟[0-9]+/}表示将满足"张伟[0-9]+"正则的数据提取为student实体的name字段 2. ${xxx}是全匹配提取表达式的简写模式,该表达式将任意内容提取到xxx 3. ${/regex/}非获取匹配表达式,该表达式只会检验数据是否匹配regex正则,但不会保存匹配到的内容 4. 当对数据实体使用"[index]"作为后缀时,表明这是一个列表,引擎会将连续多行匹配的结果填充为一个列表,并自动处理一对多关系 例如:${students[i].name/张伟[0-9]+/}会将students创建为List<Student> *注意:提取表达式不支持多维数组,并且引擎会自动忽略自增变量的名称,引擎在解析时会将自增变量抹除 例如:${students[i].indexs[j]}会被处理为${students[].indexs[]} 但是为了更好的表意性推荐将索引变化情况相同的实体使用相同名称的自增变量 *注意:提取模板是不匹配样式的