JQuick-Excel 实战:FORMAT 管理日期与数字的 Excel 显示
JQuick-Excel 实战FORMAT 管理日期与数字的 Excel 显示tags: Java, Excel, JQuick-Excel, 报表导出, FORMAT, 日期格式, 数字格式更多示例: JQuick-Excel 官网示例 https://www.jquick.org/jquick-excel/#demo-formulas0005简介日期显示成连续数字、金额缺少分组分隔符、比例被看成普通小数常常不是源数据本身错误而是工作簿没有得到合适的显示格式。JQuick-Excel 的导出 DSL 提供FORMATREADME-CN 将其定义为 Excel 显示格式并给出FORMAT{amount:currency}、FORMAT{enrollmentDate:yyyy-MM-dd}等写法。本文以这些已确认事实为界说明如何把字段级显示规则写入EXPORT WITH并区分FORMAT、TRANSFORM、STYLE各自承担的责任。本文使用 Mavenio.github.paohaijiao:jquick-excel:3.6.0与 Java 8。事实仅来自 README-CN 和src/test/resources/jquick-excel.xml测试 XML 中出现了dateFormat(${enrollmentDate},yyyy-MM-dd)的转换README-CN 明确说明FORMAT独立负责转换后的 Excel 单元格显示。因此不能把FORMAT误写成未知的类型转换、模板替换或单元格公式能力。前言Excel 的值与显示不是同一件事。业务数据可以是日期、数值或其他类型用户在网格中看到的内容则由单元格显示规则决定。一个日期可以按年、月、日显示也可以携带时间一个数值可以显示小数位、分组符号或百分号。导出报表如果只关注“看起来像”很容易在 Java 侧提前把值拼成文本。这样得到的文件虽然视觉上可能暂时正确却可能损失后续筛选、排序、统计和公式计算需要的数值或日期语义。FORMAT的价值就在于将展示要求声明在导出规则中。MAPPING负责源字段到表头的映射FORMAT负责字段在 Excel 中的显示TRANSFORM负责值表达式转换STYLE负责样式目标。把这些职责拆开阅读 XML 时能快速定位问题列标题不对先看MAPPING日期或数值外观不对先看FORMAT字段值被翻译或改写先看TRANSFORM字体与对齐等视觉效果再看STYLE。README-CN 对导出的描述包括工作表、表头、映射、格式、转换、公式、样式、合并、图表和页脚。这里的“格式”不是泛指所有视觉属性而是表格中单独的FORMAT配置项。它面向字段名而不是行号、列字母或表头文字。因而调整字段名时应同步检查MAPPING、FORMAT和TRANSFORM只改表头文案时通常不应把字段级显示规则一起改成表头名称。环境与依赖README-CN 明确 JQuick-Excel 支持xls和xlsx要求 Java 8。本文使用其公开的 Maven 坐标。dependencygroupIdio.github.paohaijiao/groupIdartifactIdjquick-excel/artifactIdversion3.6.0/version/dependency导出规则由 XML 中的excel承载EXPORT WITH下可同时声明工作表、表头、映射和显示格式。下例遵循 README-CN 的字段到表头映射方向左侧是源字段名右侧是输出表头。FORMAT的键仍然是左侧字段名。?xml version1.0 encodingUTF-8?!DOCTYPEexcelsPUBLIC-//PAOHAIJIAO//DTD API EXCEL 1.0//ENclasspath:paohaijiao/dtd/Jquick-excel.dtdexcelsnamespacecom.example.excel.OrderExcelServiceexcelnameexportOrdersreturnClassvoid![CDATA[ EXPORT WITH SHEET订单报表, HEADERtrue, MAPPING{ orderNo:订单号, orderDate:下单日期, amount:订单金额, quantity:数量, completionRate:完成率 }, FORMAT{ orderDate:yyyy-MM-dd, amount:currency } ]]/excel/excels项目测试资源的导出 XML 也说明了另一项关键边界enrollmentDate可在TRANSFORM中使用dateFormat(${enrollmentDate},yyyy-MM-dd)同时 README-CN 又给出FORMAT{enrollmentDate:yyyy-MM-dd}。两种配置都与日期有关但目标不相同。本文中只要需求是 Excel 的显示形式就优先用FORMAT只有确实需要求值器把字段值转换为另一个值时才讨论TRANSFORM。代码示例Java 侧根据 README-CN 的 XML 导出链路准备行数据和输出流。数据通过JObjectConverter与JQuickRow.toRows转为行再将行和输出流交给JQuickExcelExportXmlParseFactory。JQuickXmlFactory读取 XML 后创建接口代理。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,ObjectdataloadOrders();ListJQuickRowrowsJQuickRow.toRows(JObjectConverter.convert(data));try(OutputStreamoutputnewFileOutputStream(orders.xlsx)){JQuickParseHandlerparsernewJQuickExcelExportXmlParseFactory(rows,output);JQuickFactoryfactorynewJQuickXmlFactory(parser,jquick-excel.xml);OrderExcelServiceservicefactory.createApi(OrderExcelService.class);service.exportOrders(field,value);}字段值应按业务语义准备。日期字段应保留为应用中可正确导出的日期值金额、数量和比例也应保留可计算的数值含义不要为了获得逗号或百分号而在数据准备阶段先拼接展示文本。FORMAT的作用正是在输出工作簿时处理显示层。下例仅表达字段数据与 XML 字段名的对应关系没有把显示内容预先写进amount或completionRate。importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;ListMapString,ObjectdatanewArrayList();MapString,ObjectordernewLinkedHashMap();order.put(orderNo,SO-1001);order.put(orderDate,orderDateValue());order.put(amount,amountValue());order.put(quantity,12);order.put(completionRate,rateValue());data.add(order);日期字段最直接的已确认示例是yyyy-MM-dd。README-CN 同时在TRANSFORM部分给出dateFormat(${enrollmentDate},yyyy-MM-dd)并将FORMAT描述为最终 Excel 显示控制。因此若文件只要求“日期列显示为年-月-日”可集中使用字段级格式规则。excelnameexportStudentsreturnClassvoid![CDATA[ EXPORT WITH SHEET学生表, HEADERtrue, MAPPING{ id:主键, name:姓名, enrollmentDate:入学时间 }, FORMAT{ enrollmentDate:yyyy-MM-dd } ]]/excel当业务确实需要转换值时再加TRANSFORM。README-CN 已确认内置dateFormat、toUpper和trans并说明${field}读取当前行字段、${key}可读取JContext值。下面的规则展示三种配置并列存在的清晰方式TRANSFORM改变姓名和性别字段的输出值FORMAT负责日期显示二者不承担对方职责。excelnameexportStudentsreturnClassvoid![CDATA[ EXPORT WITH SHEET学生表, HEADERtrue, MAPPING{ name:姓名, gender:性别, enrollmentDate:入学时间 }, FORMAT{enrollmentDate:yyyy-MM-dd}, TRANSFORM{ name:toUpper(${name}), gender:trans(${dict},${gender}) } ]]/excelJava 侧若使用字典转换可按 README-CN 将字典放入JContext。日期显示格式仍留在 XML 的FORMAT而不要求把日期先转换成字符串。importcom.github.paohaijiao.context.JContext;MapString,ObjectgendernewLinkedHashMap();gender.put(1,Male);gender.put(0,Female);JContextcontextnewJContext();context.put(dict,gender);原理说明FORMAT是字段级的 Excel 显示规则。README-CN 的导出参数表把它列为“定义 Excel 显示格式”并提供FORMAT{amount:currency}与FORMAT{date:yyyy-MM-dd}的用法。它与MAPPING的关系是同一个字段先通过映射决定写到哪个表头列再由格式决定该字段单元格的显示方式。它不是坐标定位 DSL所以不应将A2、ROW 1等样式目标写进字段格式配置。日期显示应与值转换区分。测试 XML 里的dateFormat(${enrollmentDate},yyyy-MM-dd)属于TRANSFORM表达式README-CN 也明确写出TRANSFORM为每一行计算表达式。FORMAT则“独立负责转换后的 Excel 单元格显示格式”。如果目标是保留字段的日期语义同时让用户看到特定形式字段格式比把日期预先改为文本更符合这一定义。数值也是同一原则。金额、数量和比例的显示要求不等于字段值的计算要求。README-CN 以FORMAT{amount:currency}展示金额字段的格式配置。实际采用何种数值展示口径应由业务决定并应先统一输入数据含义再配置对应的 Excel 显示规则。不要依赖显示规则修复已经以错误量纲传入的数值。STYLE与FORMAT的分工也需要明确。README-CN 的样式参数包括字体、字号、粗体、斜体、颜色、对齐、边框和填充等同时列出了dataFormatString。但 README-CN 也专门为FORMAT提供字段级日期和数字显示入口。对于常规字段展示使用FORMAT可以直接表明意图对于明确坐标的视觉布局再使用STYLE。同一字段不要在多处重复定义相同的显示语义否则维护者无法判断哪份配置是权威来源。TRANSFORM的值转换可以使用当前字段和上下文。README-CN 的链路是当前行字段或JContext值经过${field}解析进入TRANSFORM表达式和函数查找得到转换结果最后由FORMAT控制 Excel 显示。理解这个顺序有助于排障字典文本不正确时检查上下文和trans日期视觉效果不正确时检查FORMAT字段根本没有出现在目标列时检查MAPPING。注意事项第一FORMAT用于显示不是数据清洗机制。格式字符串不能将任意文本自动变为可计算金额也不能替代业务层对空值、非法值和默认值的处理。源数据应先符合字段语义。第二格式键必须使用导出源字段名。README-CN 的MAPPING{id:ID}表示左边是字段、右边是表头FORMAT{amount:currency}同样以字段为键。把表头文字或 Excel 列字母写成格式键会破坏这一契约。第三日期格式要与模板示例和业务口径保持一致。yyyy-MM-dd是 README-CN 已确认的形式。涉及日期时间、不同区域显示或复杂格式时应以真实工作簿在目标 Excel 客户端验证而不要仅以 XML 可解析作为验收结论。第四避免同时把日期转换为文本又再对其声明日期显示格式。若必须使用dateFormat做值转换应清楚说明输出预期若只需要 Excel 外观优先使用FORMAT。同一字段只保留一个主要展示策略后续维护才可追踪。第五比例、金额和数量应使用真实样本验收。至少覆盖零值、整数、带小数的值、大值、空值以及业务允许的边界值。验收不应只看截图还要检查筛选、排序和公式计算是否仍符合报表目的。第六FORMAT与主题、页脚、公式、样式可以同处一个EXPORT WITH规则但它们是不同能力。修改格式时不要顺手改变TRANSFORM或STYLE的语义保持改动范围小有助于定位导出回归。总结日期和数字列的首要判断是“值是否仍保留业务语义”其次才是“用户看见什么”。FORMAT是导出字段级显示规则适合用已确认的yyyy-MM-dd或currency等形式表达外观不要为了分组符、日期写法或比例观感预先在 Java 中把可计算的值拼成文本。展示问题与源数据量纲错误是两类问题格式配置不能替后者兜底。检查时应把键名和数据链路一起看。FORMAT的键应是导出源字段而非表头文本或 Excel 列号字段重命名时需同步复核MAPPING、TRANSFORM与格式配置。日期仅需调整单元格外观时交给FORMAT确需计算出另一个字段值时才使用TRANSFORM字体、颜色和坐标布局则留给STYLE。真实文件是这类规则的最终验证对象。金额、数量和比例至少应覆盖零、整数、小数、大值、空值及业务边界并检查筛选、排序和公式计算仍符合报表目的日期还应在目标 Excel 客户端检查显示效果。不要根据 XML 能被读取便认定各客户端表现一致也不要在字段和坐标两处重复声明同一显示语义避免后续无法判断配置来源。