JQuick-Excel 实战:以 TRANSFORM、上下文与 FOOTER 组织导出渲染 JQuick-Excel 实战以 TRANSFORM、上下文与 FOOTER 组织导出渲染tags: Java, Excel, JQuick-Excel, 模板渲染, TRANSFORM, FOOTER, XML DSL更多示例: JQuick-Excel 官网示例 https://www.jquick.org/jquick-excel/#demo-formulas0005简介“Excel 模板渲染”容易被误解为拿一份任意现成的 xlsx在单元格中放置某种占位符然后由框架替换。就本项目 README-CN 与src/test/resources/jquick-excel.xml可确认的内容而言可靠的导出渲染能力是EXPORT WITH下的字段映射、TRANSFORM、FORMAT、STYLE、主题和FOOTER。README-CN 明确写出使用TRANSFORM、上下文值和FOOTER表达式根据业务数据渲染输出测试 XML 也展示了字典转换、数值转换和日期转换。本文严格限定在这些证据内。文中的“模板”指 XML 中可版本化的导出规则与主题配置不编造外部 xlsx 单元格占位符语法不假定未知的模板加载 API也不把FOOTER说成打印设置中的页眉页脚。FOOTER的已确认 DSL 是FOOTERGenerated by JQuickExcelREADME-CN 还展示了FOOTERGenerated by JQuickExcel on ${current_date()}。是否使用表达式取决于当前依赖和函数是否已确认可用。前言导出文件的内容经常需要经过展示层处理。业务对象中性别可能保存为编码日期可能需要统一显示姓名可能需要文本转换文件末尾还需要写明生成来源或日期。把这些规则散落在 Java 组装代码中会使数据准备和报表展示耦合。JQuick-Excel 的 XML DSL 可以把字段到表头的映射、字段转换、显示格式、样式和页脚集中在一个导出定义中让规则成为可审查的资源。README-CN 给出了导出配置的职责划分MAPPING将源字段映射到表头TRANSFORM在输出前转换值FORMAT定义 Excel 显示格式STYLE对目标应用样式FOOTER设置页脚文本。测试 XML 中的实际配置是gender:trans(${dict},${gender})、age:add(${age},1)、enrollmentDate:dateFormat(${enrollmentDate},yyyy-MM-dd)。这说明渲染不是未知的字符串替换而是针对当前行字段和上下文的 DSL 表达式计算。上下文使同一规则可以使用外部字典等值。README-CN 明确JContext可保存字典${field}读取当前行字段${key}可读取JContext值。于是trans(${dict},${gender})可以把当前行的gender交给dict翻译。这里的规则方向应由输出目的决定导出通常是从内部编码变成展示文字导入校验中的dict规则则是另一种输入成员关系约束不应混淆。环境与依赖本文使用 Java 8 和 JQuick-Excel3.6.0。README-CN 明确支持xls、xlsx并公开 Maven 依赖坐标。dependencygroupIdio.github.paohaijiao/groupIdartifactIdjquick-excel/artifactIdversion3.6.0/version/dependencyXML 规则放在类路径中通过namespace与服务接口关联。以下片段采用 README-CN 的结构EXPORT WITH指定表头和映射TRANSFORM处理展示值FORMAT管理 Excel 日期显示FOOTER写出收尾文本。?xml version1.0 encodingUTF-8?!DOCTYPEexcelsPUBLIC-//PAOHAIJIAO//DTD API EXCEL 1.0//ENclasspath:paohaijiao/dtd/Jquick-excel.dtdexcelsnamespacecom.example.excel.StudentReportServiceexcelnameexportStudentReportreturnClassvoid![CDATA[ EXPORT WITH SHEET学生报表, HEADERtrue, MAPPING{ id:主键, name:姓名, gender:性别, enrollmentDate:入学时间 }, FORMAT{enrollmentDate:yyyy-MM-dd}, TRANSFORM{ name:toUpper(${name}), gender:trans(${dict},${gender}) }, FOOTERGenerated by JQuickExcel ]]/excel/excels上例中的toUpper与trans都来自 README-CN 已列出的内置求值器。FORMAT并不取代TRANSFORM前者是最终 Excel 显示格式后者是字段表达式计算。页脚使用固定文本因而不会依赖当前上下文是否存在某个函数。代码示例Java 侧先准备字典并放入JContext。README-CN 已给出context.put(dict, gender)并明确导入解析器接收上下文和输入流对于导出README-CN 同样说明 Java 调用方提供业务数据、流和上下文值。下例展示已确认的上下文构造方式。importcom.github.paohaijiao.context.JContext;importjava.util.HashMap;importjava.util.Map;MapString,ObjectgendernewHashMap();gender.put(1,Male);gender.put(0,Female);JContextcontextnewJContext();context.put(dict,gender);数据准备和 XML 导出调用可沿用 README-CN 的基础入口。行数据通过JObjectConverter转换再由JQuickRow.toRows组织输出流和行数据传入JQuickExcelExportXmlParseFactoryXML 工厂创建服务代理。下例没有编造将JContext注入导出解析器的未知构造方式仍使用 README-CN 明确展示的导出构造形式上下文相关能力应在项目实际已确认的调用封装中提供。importcom.github.paohaijiao.convert.JObjectConverter;importcom.github.paohaijiao.statement.JQuickRow;importcom.github.paohaijiao.xml.JQuickFactory;importcom.github.paohaijiao.xml.JQuickXmlFactory;importcom.github.paohaijiao.xml.parse.JQuickParseHandler;importcom.github.paohaijiao.xml.parse.excel.JQuickExcelExportXmlParseFactory;importjava.io.FileOutputStream;importjava.io.OutputStream;importjava.util.List;importjava.util.Map;ListMapString,ObjectstudentsloadStudents();ListJQuickRowrowsJQuickRow.toRows(JObjectConverter.convert(students));try(OutputStreamoutputnewFileOutputStream(students.xlsx)){JQuickParseHandlerparsernewJQuickExcelExportXmlParseFactory(rows,output);JQuickFactoryfactorynewJQuickXmlFactory(parser,jquick-excel.xml);StudentReportServiceservicefactory.createApi(StudentReportService.class);service.exportStudentReport(field,value);}项目测试 XML 还给出了一个更完整的转换组合。以下规则只复用测试资源中实际出现的字段与函数形式性别使用trans年龄使用add入学日期使用dateFormat。该片段用于说明测试中存在的 DSL并不要求每份报表都进行年龄加一或日期文本转换。excelnameexportExcelreturnClassvoid![CDATA[ EXPORT WITH SHEET学生表, HEADERtrue, MAPPING{ id:主键, name:姓名, gender:性别, age:年龄, enrollmentDate:入学时间 }, TRANSFORM{ gender:trans(${dict},${gender}), age:add(${age},1), enrollmentDate:dateFormat(${enrollmentDate},yyyy-MM-dd) }, FOOTERGenerated by JQuickExcel ]]/excelREADME-CN 还展示页脚表达式形式。只有在当前项目确认表达式函数可用时才应采用这种写法本文不把它扩展为外部 xlsx 的占位符语法。它仍然是 XML DSL 中FOOTER的值。excelnameexportReportreturnClassvoid![CDATA[ EXPORT WITH SHEETReport, HEADERtrue, MAPPING{name:Name,gender:Gender}, TRANSFORM{gender:trans(${dict},${gender})}, FOOTERGenerated by JQuickExcel on ${current_date()} ]]/excel主题也是 README-CN 确认的导出能力。文档列出 42 个内置主题并展示在执行规则后、创建导出处理器前通过JExcelExportModel设置主题的示例。以下 Java 代码仅复用 README-CN 已给出的类、调用顺序与主题设置方式。JExcelExportModelconfig(JExcelExportModel)executor.execute(rule);config.setTheme(jade);JExcelExportHandlerhandlernewJExcelExportHandler(config,data);Workbookworkbookhandler.getWorkBook();workbook.write(outputStream);主题、TRANSFORM、FORMAT和FOOTER可以共同组成一份配置驱动的导出规则主题统一视觉基调映射决定列转换决定展示值格式决定 Excel 单元格显示页脚补充收尾文本。但这不等于框架已经公开支持读取任意 xlsx 并扫描替换其中的占位符。没有可执行证据时应将需求限制为已确认 DSL或另行补充专用模板能力的验证。原理说明README-CN 描述的运行链路是当前行字段或JContext值通过${field}进入TRANSFORM表达式函数查找后调用提供者得到转换结果最终由FORMAT控制 Excel 显示。内置求值器包含toUpper、dateFormat、trans。项目测试 XML 中实际使用了trans、add、dateFormat可作为当前项目配置示例的证据。MAPPING在这条链路中负责字段到表头的映射。README-CN 的导出示例为MAPPING{id:ID,name:Name}即左侧是数据源字段、右侧是输出列头。TRANSFORM仍以左侧字段为目标例如gender:trans(${dict},${gender})FORMAT同样以字段为键例如enrollmentDate:yyyy-MM-dd。这三个配置应保持字段键一致避免因重命名 DTO 属性或调整数据转换层而产生静默偏差。JContext是外部值容器。README-CN 的示例创建字典后调用context.put(dict, gender)表达式再通过${dict}取用。当前行字段和上下文键有不同来源${gender}是行字段${dict}是上下文。编写trans(${dict},${gender})时应明确字典中的键和值对应什么展示方向并用真实数据验证编码和显示文字是否一致。FOOTER是导出 DSL 的一个独立参数。README-CN 给出固定文本FOOTERGenerated by JQuickExcel也给出带${current_date()}的文本表达式并说明它可渲染文本或变量表达式。因此最稳妥的默认方式是固定文本使用表达式时先确认当前版本和依赖中相应函数已可用。不能把 DSL 文本中的${...}推导为任意 xlsx 单元格都可替换的语法。主题则是模型层面的导出视觉配置。README-CN 的例子是在解析规则获得JExcelExportModel后调用config.setTheme(jade)再创建JExcelExportHandler和工作簿。主题 code 应来自 README-CN 列出的内置名称例如default、jade、oceanBlue、royalGold等。主题不改变MAPPING、TRANSFORM或FOOTER的业务含义它只服务于已确认的导出视觉主题设置。注意事项第一不要编造外部 xlsx 占位符语法。README-CN 和测试 XML 证明的是 XML DSL 中的${field}、${key}、TRANSFORM、FOOTER表达式和主题设置而不是“在任意 xlsx 单元格写${name}即会替换”。遇到这种需求应先补充可执行证据或采用已验证的专用方案。第二TRANSFORM与FORMAT不可混淆。值确实需要翻译、大写或日期格式化时使用TRANSFORM仅需要 Excel 显示日期或数字时使用FORMAT。同一字段同时在多处反复承担显示职责会降低规则可读性。第三字典方向必须以输出目标为准。trans(${dict},${gender})读取上下文字典和当前行字段字典内容应从内部值映射到用户要看到的文字。导入中的dict校验规则属于另一个问题不能不经检查地复用同一映射方向。第四页脚文本是报表内容的一部分。若报表还需要合计、公式或合并区域应在设计时明确它们与FOOTER的顺序并用真实文件验证最终布局。本文只确认FOOTER的 DSL 文本能力不假定未公开的打印页眉页脚行为。第五主题 code 使用 README-CN 已列出的名称。拼写、依赖版本和实际输出效果均应在目标环境验证。主题是视觉选择不应隐藏字段口径、审核状态或其他必须以数据列明确表达的业务信息。第六流、数据和上下文的生命周期应由调用端明确管理。README-CN 的导出示例使用 try-with-resources 关闭输出流导入示例在输入流打开期间执行代理调用。实际工程应沿用该资源管理原则而不要依赖未确认的自动关闭行为。总结这里的“模板渲染”应落在已经确认的 XML 导出规则上MAPPING决定列与表头TRANSFORM基于当前行和JContext计算展示值FORMAT管理单元格显示主题处理统一视觉FOOTER补充收尾文本。它的优点是配置可版本化、规则可审查真正需要变化的是字段展示或报表布局时应先定位到对应配置项而不是把处理散进数据组装代码。字典转换是最需要验证方向的一环。${gender}是当前行字段${dict}是上下文值trans(${dict},${gender})的结果应以真实编码和展示文字核对。页脚使用固定文本最容易保持确定性采用${current_date()}一类表达式前应确认当前版本和依赖中函数可用。主题也只改变外观不能取代报表中需要明确呈现的业务字段。使用边界必须清晰已有证据支持 DSL 内的表达式取值与FOOTER文本不支持据此推导任意外部 xlsx 单元格占位符都会被扫描替换。复杂模板需求应先补充可执行验证。导出验收则应同时检查转换结果、日期与数字显示、页脚位置和主题效果并确保输出流、行数据及上下文在执行期间都由调用端按明确生命周期管理。