JQuick-Excel FORMAT 导出格式实战:日期、金额显示与 TRANSFORM 的边界

JQuick-Excel FORMAT 导出格式实战:日期、金额显示与 TRANSFORM 的边界

tags: #JQuickExcel #Java #Excel导出 #FORMAT #XMLDSL

简介

本文依据 README-CN.md 中的 FORMAT、TRANSFORM 说明和测试 XML 的导出规则,讲解如何在 EXPORT WITH 中为字段声明 Excel 显示格式,并通过 JObjectConverter、JQuickRow、JQuickExcelExportXmlParseFactory、JQuickXmlFactory 完成可验证的报表导出。

前言

很多 Excel 导出问题并不是值错误,而是显示方式不符合使用者预期。日期可能显示为不易阅读的数字,金额缺少小数位或千分位,比例显示为 0.875 而不是百分比。一个常见但不理想的应对方式是在 Java 中提前把值拼成字符串。这样表面上好看了,却可能破坏 Excel 中继续排序、求和、筛选和计算的使用方式。

README-CN.md 将这种职责分开:FORMAT 定义 Excel 显示格式,TRANSFORM 在导入或导出前计算字段值。README 的导出示例给出 FORMAT={"amount":"currency"},并在类型转换章节明确说明 FORMAT 独立负责最终 Excel 单元格显示;测试 XML 则给出了 TRANSFORM 对 gender、age、enrollmentDate 的已用形式。本文围绕这一边界展开,不把 FORMAT 说成万能数据转换器,也不捏造项目资料未列出的自动格式推断或内部样式策略。

正确的问题顺序是:字段的业务值是否正确?如果正确,只是要改变 Excel 的呈现方式,使用 FORMAT;如果需要将值转换为另一个值,例如字典映射、大小写调整、日期文本计算,才考虑 README 中已有的 TRANSFORM 表达式。两者可以出现在同一规则,但应明确每一项的目的,避免让同一字段既被当作原始日期又被强制当作文本而难以验收。

本文以订单报表为例。orderDate 是日期值,amount 是数值,completionRate 是小数比例。MAPPING 决定列标题,FORMAT 决定显示样式,Java 仍准备字段和值,XML 代理负责写出工作簿。示例的格式串是 Excel 显示格式示例;投入生产前应以目标模板和实际 Excel 客户端进行验收。

环境与依赖

README-CN.md 标明 JQuick-Excel 要求 Java 8+,可处理 xls 与 xlsx。以下使用 README 所列 Maven 坐标及版本。XML 需放在 src/main/resources/jquick-excel.xml,以便 JQuickXmlFactory 用资源名加载。

xml 复制代码
<dependency>
  <groupId>io.github.paohaijiao</groupId>
  <artifactId>jquick-excel</artifactId>
  <version>3.6.0</version>
</dependency>

导出前应确认四类前提。第一,服务接口全限定名和 XML namespace 一致,方法名和 excel name 一致。第二,Java 数据 Map 具有 orderNo、orderDate、amount、completionRate 等字段。第三,输出位置可以创建文件。第四,实际接收方认可所使用的日期、金额、百分比显示格式。

README 还展示了 FORMAT={"enrollmentDate":"yyyy-MM-dd"}。这说明日期字段格式化是已列出的用法。对于 README 没有枚举的复杂格式、特殊货币语义和跨区域显示差异,不应在基础文章中承诺框架会自动判断;应以真实文件测试为准。

代码示例

XML 定义"订单报表"页,输出标题,并为三个字段声明 FORMAT。MAPPING 左侧是 Java 行字段,右侧是 Excel 标题。FORMAT 的键同样采用字段名,因此应与 MAPPING 左侧对齐。这里的 yyyy-MM-dd 用于日期显示,#,##0.00 用于两位小数显示,0.00% 用于比例显示。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE excels PUBLIC "-//PAOHAIJIAO//DTD API EXCEL 1.0//EN"
        "classpath:paohaijiao/dtd/Jquick-excel.dtd">
<excels namespace="com.example.OrderExcelService">
    <excel name="exportOrders" returnClass="void">
        <![CDATA[
            EXPORT WITH
            SHEET="订单报表",
            HEADER=true,
            MAPPING={
                "orderNo":"订单号",
                "orderDate":"下单日期",
                "amount":"金额",
                "completionRate":"完成率"
            },
            FORMAT={
                "orderDate":"yyyy-MM-dd",
                "amount":"#,##0.00",
                "completionRate":"0.00%"
            }
        ]]>
    </excel>
</excels>

接口使用 README 中的 XML 服务代理形式。业务调用不直接传递格式串;格式作为模板规则固定在 XML 中,代码只准备行数据和输出流。这样格式修改可以独立于业务查询逻辑进行审查。

java 复制代码
import com.github.paohaijiao.xml.param.Param;

public interface OrderExcelService {
    void exportOrders(@Param("field") String field, @Param("value") String value);
}

Java 侧保留日期和数值对象。金额使用 BigDecimal,比例使用 BigDecimal("0.875"),含义是 87.5%,与 0.00% 的显示目的相匹配。再通过 README 提供的 JObjectConverter.convert 和 JQuickRow.toRows 形成导出行。

java 复制代码
import com.github.paohaijiao.convert.JObjectConverter;
import com.github.paohaijiao.statement.JQuickRow;
import com.github.paohaijiao.xml.JQuickFactory;
import com.github.paohaijiao.xml.JQuickXmlFactory;
import com.github.paohaijiao.xml.parse.JQuickParseHandler;
import com.github.paohaijiao.xml.parse.excel.JQuickExcelExportXmlParseFactory;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Map<String, Object> order = new LinkedHashMap<>();
order.put("orderNo", "SO-1001");
order.put("orderDate", new Date());
order.put("amount", new BigDecimal("123456.7"));
order.put("completionRate", new BigDecimal("0.875"));

List<JQuickRow> rows = JQuickRow.toRows(
        JObjectConverter.convert(Collections.singletonList(order)));
try (OutputStream output = new FileOutputStream("orders.xlsx")) {
    JQuickParseHandler parser = new JQuickExcelExportXmlParseFactory(rows, output);
    JQuickFactory factory = new JQuickXmlFactory(parser, "jquick-excel.xml");
    OrderExcelService service = factory.createApi(OrderExcelService.class);
    service.exportOrders("field", "value");
}

若业务需要先做字段转换,再声明显示格式,可参考 README 中已出现的组合:TRANSFORM 使用 dateFormat、trans 或 toUpper,而 FORMAT 负责最终单元格显示。例如 README 公开了以下形式。是否同时配置同一字段,应由目标值类型和集成测试决定,不应仅为了让 XML 更复杂而叠加规则。

xml 复制代码
<excels namespace="com.example.OrderExcelService">
    <excel name="exportOrders" returnClass="void">
        <![CDATA[
            EXPORT WITH
            SHEET="订单报表",
            HEADER=true,
            MAPPING={"gender":"性别","enrollmentDate":"入学时间"},
            FORMAT={"enrollmentDate":"yyyy-MM-dd"},
            TRANSFORM={
                "gender":trans(${dict},${gender}),
                "enrollmentDate":dateFormat(${enrollmentDate},'yyyy-MM-dd')
            }
        ]]>
    </excel>
</excels>

上段 DSL 的 trans、dateFormat 均来源于 README 和测试 XML。它展示的是已列出的能力边界,不代表所有字段都应同时转换和格式化。尤其是日期字段,若业务希望 Excel 保留日期类型,应该围绕真实输出结果选择规则,而不是只看字符串外观。

原理说明

调用 exportOrders 时,JQuickXmlFactory 读取资源中的 XML,并根据 namespace、接口和方法名选择对应的 EXPORT WITH 规则。JQuickExcelExportXmlParseFactory 在构造时已经拿到 List<JQuickRow> 和输出流,因此规则能逐行处理 Java 准备的字段值并写出工作簿。

MAPPING 先决定字段放入哪一列及列标题。对于 "amount":"金额",当前行的 amount 值写到"金额"列。HEADER=true 使标题位于第一行。FORMAT={"amount":"#,##0.00"} 再对这个字段的 Excel 单元格声明显示方式。依据 README 的职责说明,FORMAT 用于最终显示,而非在 Java 层重写业务字段。

日期也是同样的分层:orderDate 是数据字段,"下单日期"是标题,yyyy-MM-dd 是显示格式。值、标题和显示模式分别由 Java Map、MAPPING 右侧、FORMAT 三处承担。分开配置的好处是当产品把标题改为"创建日期"时,不需要把业务字段改名;当财务把金额显示改为另一种模式时,也不需要改动查询数据。

TRANSFORM 与 FORMAT 的不同在于它计算字段值。README 列出 toUpper、dateFormat、trans 等函数,并说明 ${field} 读取当前行字段、${key} 可读取 JContext 值。测试 XML 也使用 trans(${dict},${gender})、add(${age},1)、dateFormat(${enrollmentDate},'yyyy-MM-dd')。这意味着转换规则应以"值需要变化"为出发点,FORMAT 应以"Excel 如何显示"为出发点。

对于百分比,输入数据语义必须和显示规则相互匹配。例中的 0.875 是比例小数,采用 0.00% 后的验收目标是 87.50%。如果业务已经传入 87.5,却继续采用百分比显示,结果可能与预期不符。这里不是框架的自动纠错问题,而是业务值语义和显示格式必须由调用方明确约定。

格式也不替代数据校验。传入无法作为金额、日期理解的内容时,不能只依赖 #,##0.00 或 yyyy-MM-dd 让它变成正确业务值。应先保证 Java 数据满足业务含义,再让 FORMAT 管理 Excel 呈现。README 所述 FORMAT 是输出规则的一部分,不应被用于掩盖上游数据质量问题。

注意事项

第一,FORMAT 键应与 MAPPING 左侧字段一致。XML 写 "amount":"金额" 时,格式也写 "amount":"#,##0.00",不要使用标题"金额"作为 FORMAT 键。字段名负责规则定位,标题只是工作簿展示文本。

第二,保留合适的业务值类型。日期、金额、比例应尽量以能表达业务含义的 Java 值传入,再由 FORMAT 表示显示方式。不要仅为了出现千分位或百分号而提前拼接字符串,除非业务本身就需要文本值。

第三,区分 FORMAT 和 TRANSFORM。值不变、只改变单元格显示时使用 FORMAT;需要字典转换、大小写处理或日期值转换时,再使用 README 已列出的 TRANSFORM 函数。不要默认每个格式字段都要加 TRANSFORM。

第四,百分比要先统一语义。示例 0.875 搭配 0.00% 的验收结果是 87.50%。在业务层明确输入是 0 到 1 的比例,还是已经乘以 100 的数值,再决定格式,避免报表数值被放大或缩小。

第五,FORMAT 位于 EXPORT WITH 的规则中。本文不把它用于导入规则,也不描述未在给定 README 和测试 XML 中验证的全局格式、自动列宽、主题或私有样式实现。需求超出字段显示格式时,应先回到项目已有文档和测试确认范围。

第六,检查 XML 代理链路。资源文件必须在类路径,namespace 和接口全限定名一致,excel name 与方法名一致,JQuickExcelExportXmlParseFactory 创建时需要行数据和尚未关闭的输出流。

第七,验收要在 Excel 中实际检查。导出后确认"订单报表"首行标题,检查日期是否以预期模式显示,金额是否有两位小数及千分位,0.875 是否显示为 87.50%。同时尝试对金额列求和、对日期列排序,以确认使用体验是否满足报表要求。

总结

FORMAT 面向 Excel 单元格的呈现方式,适合把已准备的日期、金额和比例按模板要求显示;字段取值和列标题仍分别由 Java 数据与 MAPPING 决定。这样报表的值语义、列结构和视觉格式能够独立调整,不必为了显示效果预先拼接文本。

值需要改变时才使用已确认的 TRANSFORM,仅改变显示时保留给 FORMAT。验证不能停留在屏幕外观,还应在实际工作簿中检查日期排序、金额求和和比例显示是否符合业务语义,确保格式规则没有掩盖数据类型或数值约定的问题。

相关推荐
言乐69 小时前
HTML视频审核模型
python·django·virtualenv·pygame·tornado
言乐610 小时前
Python根据无法识别搜索词找出可能输入内容模型
开发语言·python·django·virtualenv·pygame
沙漠之主10 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
hasty10 小时前
不上传新包,也能改变用户拿到的版本:npm dist-tag 的 OIDC 权限治理
前端·npm·node.js
yl453011 小时前
硫酸泄露处理生产商怎么选才够专业
大数据·人工智能·python
笨笨饿11 小时前
140_AI新手村MCP与Skills是干嘛的
开发语言·人工智能·python·stm32·单片机·嵌入式硬件·物联网
Csvn11 小时前
diff 算法(虚拟 DOM Reconciliation)
前端
for_ever_love__11 小时前
机器学习入门——手写线性回归与梯度下降
人工智能·python·学习·机器学习·线性回归
打工仔折腾 AI12 小时前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战
I Am a robert girl12 小时前
当传感器学会“说谎“:拆解可靠性门控的稀疏惯性动捕融合
python·姿态估计·传感器融合·惯性动捕·imu传感器·可靠性门控·可穿戴计算