news 2026/10/18 5:22:23

JQuick-Excel 实战:用 VALIDATION 建立可维护的 Excel 导入校验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JQuick-Excel 实战:用 VALIDATION 建立可维护的 Excel 导入校验

JQuick-Excel 实战:用 VALIDATION 建立可维护的 Excel 导入校验

tags: Java, Excel, JQuick-Excel, 数据导入, VALIDATION, XML DSL

更多示例: JQuick-Excel 官网示例 https://www.jquick.org/jquick-excel/#demo-formulas0005

简介

Excel 导入的难点通常不在“能否读到单元格”,而在于如何在业务数据进入应用之前,把模板中明显不符合约定的内容拦住。JQuick-Excel 在IMPORT WITH中提供VALIDATION,用于把校验规则应用到选定的工作表位置。README-CN 已明确:规则目标可以是行、列、单元格或矩形范围;导入规则还可以组合SHEET、HEADER、MAPPING与TRANSFORM。这使得表头、字段映射和输入约束能够写在同一份 XML 服务定义中。

本文只以 README-CN 和项目的src/test/resources/jquick-excel.xml为事实依据。重点是解释已确认的范围语法、20 个规则名称及其参数形式,并给出 XML 和 Java 调用示例。文中不引入未确认的规则名、异常类型、回调机制或外部模板语法。对于数据库重复、权限、关联数据和事务等问题,仍应由导入后的 Java 业务逻辑负责。

前言

面向业务人员的 Excel 模板既方便又开放。用户可以复制内容、修改列顺序、手输日期、把数值输入为文本,也可以在同一列混用空值和非空值。如果应用只是把工作簿读成对象,再直接交给后续服务,错误会被推迟到很晚才暴露:日期转换失败、数字无法参与计算、字典外的值进入业务流程,或者长文本在下游接口处被拒绝。

将基础校验写进导入 DSL 的价值,在于让模板约定可见。SHEET指明工作表,HEADER=true声明首行是表头,MAPPING将表头映射到目标字段,VALIDATION再按 Excel 坐标选择需要检查的位置。使用者看到的模板、测试人员检查的规则、开发人员加载的 XML,围绕的是同一份配置契约。

校验应保持责任边界。单元格格式、必填、长度、日期范围、数值范围、邮箱、手机和字典成员关系,适合在VALIDATION中描述。工号是否已存在、当前操作者是否有权限、关联组织是否存在、同一文件是否存在跨行冲突以及落库事务,则不应被误认为单元格规则可以替代。导入通过后拿到List<JQuickRow>,业务层仍需要继续执行系统一致性检查。

README-CN 给出了完整的基础范围示例:ROW 5、ROW 1..10、COL A、COL A..D、C1、A1:B5。因此配置前的第一步不是挑选规则名,而是确定模板的真实布局。只要表头从第一行改到第二行,原来的A2:A100就可能需要整体移动。规则本身正确而坐标错误,仍然会导致错误的导入结果。

环境与依赖

本文使用 Java 8 或更高版本,以及 JQuick-Excel3.6.0。README-CN 明确该版本支持xls与xlsx工作簿,Maven 坐标如下。

<dependency><groupId>io.github.paohaijiao</groupId><artifactId>jquick-excel</artifactId><version>3.6.0</version></dependency>

XML 文件应放在类路径中。README-CN 的示例使用<excels>的namespace指定服务接口,并通过<excel name="...">将 DSL 规则绑定到接口方法。CDATA 可避免 XML 解析器把 DSL 内的花括号、引号和${...}当作普通 XML 内容处理。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEexcelsPUBLIC"-//PAOHAIJIAO//DTD API EXCEL 1.0//EN""classpath:paohaijiao/dtd/Jquick-excel.dtd"><excelsnamespace="com.example.excel.EmployeeExcelService"><excelname="importEmployees"returnClass="java.util.List"><![CDATA[ IMPORT WITH SHEET='员工导入', HEADER=true, MAPPING={"工号":"code","姓名":"name","年龄":"age"} ]]></excel></excels>

导入时,README-CN 已确认JQuickExcelImportXmlParseFactory接收JContext和输入流,JQuickXmlFactory根据 XML 创建服务代理。JContext可提供转换所需的上下文值;即使本次校验没有使用字典,也可以沿用同一调用结构。输入流应在代理方法执行期间保持可用,因此使用 try-with-resources 管理更稳妥。

importcom.github.paohaijiao.context.JContext;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.JQuickExcelImportXmlParseFactory;importjava.io.InputStream;importjava.util.List;try(InputStreaminput=getClass().getClassLoader().getResourceAsStream("employees.xlsx")){JContextcontext=newJContext();JQuickParseHandlerparser=newJQuickExcelImportXmlParseFactory(context,input);JQuickFactoryfactory=newJQuickXmlFactory(parser,"jquick-excel.xml");EmployeeExcelServiceservice=factory.createApi(EmployeeExcelService.class);List<JQuickRow>rows=service.importEmployees("field","value");consume(rows);}

以上 Java 代码遵循 README-CN 的 XML 导入链路。示例中的field与value是已公开接口示例中的参数,不应被理解为VALIDATION的隐含变量。规则是否命中依赖 XML 是否加载、方法名是否匹配、工作表和表头是否与 DSL 一致,以及范围是否指向正确位置。

代码示例

下面是一份围绕员工导入模板的规则。它只使用 README-CN 已列出的regex、max_length、email、integer、date_format、dict与mobile。每个规则使用required和msg;需要额外信息时使用map。数据从第 2 行开始,是因为第一行被HEADER=true作为表头处理。

<excelname="importEmployees"returnClass="java.util.List"><![CDATA[ IMPORT WITH SHEET='员工导入', HEADER=true, MAPPING={ "工号":"code", "姓名":"name", "邮箱":"email", "年龄":"age", "入职日期":"hireDate", "状态":"status", "手机":"mobile", "备注":"remark" }, VALIDATION={ A2:A500:{regex{required:true,msg:'工号格式不正确',map:{pattern:'^EMP\\d+$'}}}, B2:B500:{max_length{required:true,msg:'姓名长度不能超过 30',map:{maxLength:30}}}, C2:C500:{email{required:true,msg:'邮箱格式无效'}}, D2:D500:{integer{required:true,msg:'年龄必须为整数'}}, E2:E500:{date_format{required:true,msg:'入职日期必须为 yyyy-MM-dd',map:{'format':'yyyy-MM-dd'}}}, F2:F500:{dict{required:true,msg:'状态不在允许范围内',map:{'启用':'ENABLED','停用':'DISABLED'}}}, G2:G500:{mobile{required:false,msg:'手机号码格式无效'}}, H2:H500:{max_length{required:false,msg:'备注长度不能超过 200',map:{maxLength:200}}} } ]]></excel>

数值与日期常常同时需要“类型或格式”以及“范围”两层约束。README-CN 中,min_value使用minValue,max_value使用maxValue;min_date使用minDate,max_date使用maxDate,并同时提供format。以下示例把同一范围上的规则组合起来,文案分别指出类型和边界问题。

<excelname="importEmployeeLimits"returnClass="java.util.List"><![CDATA[ IMPORT WITH SHEET='员工导入', HEADER=true, MAPPING={"年龄":"age","转正日期":"regularDate"}, VALIDATION={ A2:A500:{ integer{required:true,msg:'年龄必须为整数'}, min_value{required:true,msg:'年龄不能小于 16',map:{minValue:16}}, max_value{required:true,msg:'年龄不能大于 70',map:{maxValue:70}} }, B2:B500:{ date_format{required:true,msg:'转正日期必须为 yyyy-MM-dd',map:{'format':'yyyy-MM-dd'}}, min_date{required:true,msg:'转正日期不能早于 2022-01-01',map:{'format':'yyyy-MM-dd',minDate:2022-01-01}}, max_date{required:true,msg:'转正日期不能晚于 2025-01-01',map:{'format':'yyyy-MM-dd',maxDate:2025-01-01}} } } ]]></excel>

范围也可以覆盖行、列或矩形区域。以下示例仅展示 README-CN 确认的ROW、COL、单元格和矩形范围写法。是否应让多个列共用同一规则,取决于模板的业务含义;名称、邮箱、日期等含义不同的列,不宜为了缩短 XML 而强行复用约束。

<excelname="importCodes"returnClass="java.util.List"><![CDATA[ IMPORT WITH SHEET='编码表', HEADER=true, MAPPING={"编码":"code","名称":"name","类型":"type"}, VALIDATION={ ROW 2..200:{min_length{required:true,msg:'数据行内容不能为空',map:{minLength:1}}}, COL A:{start_with{required:true,msg:'编码必须以 A 开头',map:{startWith:'A'}}}, C2:{contain{required:true,msg:'类型必须包含 TYPE',map:{contains:'TYPE'}}}, A2:B200:{max_length{required:true,msg:'编码或名称过长',map:{maxLength:50}}} } ]]></excel>

README-CN 还提供了程序化解析和导入处理的基础能力。它可用于验证一段 DSL 与最小工作簿,而生产工程仍可继续使用 XML 代理。下例不创造另一套规则,只是把相同的IMPORT WITH字符串交给导入执行器。

importcom.github.paohaijiao.statement.JQuickRow;importjava.util.List;Stringrule="IMPORT WITH HEADER=true, SHEET='Sheet1', "+"MAPPING={\"年龄\":\"age\"}, "+"VALIDATION={A2:A4:{integer{required:true,msg:'年龄必须为整数'}}}";// 在项目实际测试中,使用与当前版本匹配的导入执行器和处理器执行该规则。// 规则文本仍应保持 README-CN 所示的关键字、范围和参数拼写。List<JQuickRow>rows=importByConfiguredRule(rule);consume(rows);

上例刻意将项目中未由 README-CN 明确公布的具体测试构造细节留在应用已有封装中。关键是校验 DSL 本身:范围面对工作表坐标,规则面对单元格内容,Java 代码负责提供输入流、上下文和后续业务消费。

原理说明

从 README-CN 描述的能力出发,导入可以理解为一条配置驱动的链路:选择SHEET,按HEADER识别表头,用MAPPING将表头映射到字段,按需要执行TRANSFORM,并在消费结果之前把VALIDATION应用到选定范围。理解这条链路有助于定位问题。表头名称不一致时先检查MAPPING;坐标偏移时先检查HEADER与模板布局;值需要翻译时检查TRANSFORM和JContext;基础输入约束则检查VALIDATION。

范围是规则的定位方式。ROW 5指一行,ROW 1..10指连续的行区间;COL A指一列,COL A..D指连续列区间;C1是单个单元格;A1:B5是矩形区域。它们全部以 Excel 用户看到的位置为准,而不是以 Java 字段名为准。因此MAPPING={"年龄":"age"}不会让age自动成为某个坐标,年龄位于第几列仍由模板表头和映射顺序决定。

README-CN 列出的规则可以按目的理解。boolean、integer、decimal、date_format、email、mobile用于值的类型或形态;min_value、max_value、min_date、max_date、min_length、max_length用于边界;dict用于字典成员关系;regex、start_with、not_start_with、end_with、not_end_with、contain、not_contain用于文本约束。选择时优先采用业务含义最明确的规则,例如邮箱使用email,整数使用integer,日期格式使用date_format,不要把每一种输入问题都塞进难以维护的正则。

required表达是否将空值视为不可接受。必填字段可使用required:true;允许为空但一旦填写就需要符合形态的字段,可使用required:false并保留例如mobile或max_length的约束。msg是面向模板使用者的反馈,应描述可操作的修改方向。map用于规则需要的额外数据,例如日期格式、数值边界、文本长度、正则模式、前后缀或字典内容。

日期规则需要保持格式的一致性。date_format使用map:{'format':'yyyy-MM-dd'};min_date、max_date在此基础上补充minDate或maxDate。模板说明、用户填写方式、错误文案和 XML 中的格式应当一致。日期格式中的大小写也应谨慎核验,不能仅因 DSL 可被读取就假定最终输入符合要求。

字典规则的映射方向要由模板填写内容决定。若 Excel 填写的是“启用”“停用”,dict的键应对应这些可填写文本,值则可表达业务希望得到的值。它与 README-CN 中TRANSFORM={"gender":trans(${dict},${gender})}的导出展示转换属于不同语境:前者是导入校验的允许成员关系,后者是使用上下文字典转换字段值。设计时应分别确认方向,不应因为二者都出现dict就机械复用。

注意事项

第一,工作表名、表头、数据起始行和范围必须一起维护。SHEET='员工导入'、HEADER=true、MAPPING与A2:A500共同构成模板约定。表头移动、列重排或在顶部增加说明行后,所有坐标都应重新检查。

第二,只使用 README-CN 已列出的规则名和参数名。例如日期规则是date_format,数值边界是min_value、max_value,长度参数是minLength、maxLength。DSL 的关键字、花括号、冒号、逗号、引号和范围写法属于解析契约,不应按个人习惯改写。

第三,示例中的2..500是模板容量约定,而不是自动识别的最后一行。模板允许更大数据量时,范围也必须相应设计和验证。验收至少应覆盖只有表头、空值、合法值、边界值、越界值和格式错误值。

第四,避免让正则取代已有专用规则。对于邮箱、手机、整数、小数、日期、长度和数值边界,已有规则能更直接表达意图。正则更适合类似内部编码形态的约束,并应搭配清晰的msg。

第五,VALIDATION不是系统安全边界。上传文件类型、文件大小、操作者权限、数据唯一性、关联对象和事务必须继续由应用控制。即使单元格校验全部通过,也不代表数据必然满足业务状态。

第六,涉及TRANSFORM时要区分当前行字段和上下文值。README-CN 说明${field}可读取当前行字段,${key}可读取JContext中的值。先确认转换结果和校验规则的协作方式,再把它们放进同一导入流程。

总结

VALIDATION的价值不在于把所有判断塞进 XML,而在于把模板使用者能够直接修正的输入约束写清楚。邮箱、整数、日期格式、长度、数值边界和字典成员关系,应优先采用已经确认的专用规则;内部编码等文本形态再使用regex。每一条规则都应能说清目标坐标、必填要求、参数来源和失败提示,避免只留下难以解释的通用正则。

规则组合需要按字段语义验证。日期既要求格式又有上下界时,分别核对date_format、min_date、max_date的格式参数与边界值;数值则同时检查integer或decimal和min_value、max_value。验收样本至少覆盖空值、合法值、两个边界及越界或格式错误值,并确认首个数据行和最大行号与模板容量没有偏移。

这里的边界必须保持克制。通过单元格校验不代表工号唯一、关联对象存在或当前操作者有权限,文件类型、大小和落库事务也不由 DSL 取代。MAPPING决定字段落点,TRANSFORM决定已映射值如何计算,业务服务负责依赖外部状态的判断。这样安排后,模板规则既能给用户明确反馈,也不会掩盖系统层仍需承担的校验责任。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 5:20:20

从构思到打磨:我的第五个网页完整开发流程解析

把"我的第五个网页"这个标题挖开来讲&#xff0c;其实挺有意思。一个人能从第一个网页写到第五个&#xff0c;说明已经度过了最初的摸索期&#xff0c;开始真正把做网页当成一件有章法的事来对待。我记得很清楚&#xff0c;自己写到第五个页面的时候&#xff0c;最大…

作者头像 李华
网站建设 2026/10/18 5:19:02

Servlet+JSP+JDBC房屋租赁系统毕设实战指南

简介&#xff1a;本资源是一套完整的Java Web毕业设计项目——基于ServletJSPJDBC开发的房屋租赁管理系统&#xff0c;专为计算机专业本科生毕设选题及Java初学者项目实战打造&#xff0c;覆盖Web开发全流程核心技能训练。压缩包为ZIP格式&#xff0c;大小13.69MB&#xff0c;包…

作者头像 李华
网站建设 2026/10/18 5:17:32

风光储氢系统协调控制与Simulink仿真建模实战

1. 这个模型到底在解决什么问题先说结论&#xff1a;这根本不是一套"能发电又能制氢"的演示品&#xff0c;而是一个面向新能源制氢场景的系统级协调控制方案。核心目标就一句话——让风光这种靠天吃饭的不稳定电力&#xff0c;喂给一个很挑食的PEM电解槽&#xff0c;…

作者头像 李华
网站建设 2026/10/18 5:15:38

不爱吃苦的年轻人,正撑起下一个瑞幸式机会

我把“瑞幸”这个案例反复琢磨了很久&#xff0c;发现一个很有意思的事实&#xff1a;瑞幸真正跑通的核心逻辑&#xff0c;不是咖啡&#xff0c;而是“把咖啡的门槛拆掉&#xff0c;重新做给不爱吃苦的人喝”。所以当很多人问“年轻人不爱吃苦&#xff0c;还能不能喝出下一个瑞…

作者头像 李华
网站建设 2026/10/18 5:14:54

《Eigen入门》第 10 章 · const、作用域与命名空间

这一章讲三个让代码更安全、更有条理的工具&#xff1a;const&#xff08;保护数据&#xff09;、作用域&#xff08;变量在哪有效&#xff09;、命名空间&#xff08;避免名字冲突&#xff09;。理解它们&#xff0c;代码质量和可读性会上一个台阶。10.1 const&#xff1a;保护…

作者头像 李华