资讯动态

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

发布时间:2026/10/18 5:22:32 来源:尧图企业网站定制
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指明工作表HEADERtrue声明首行是表头MAPPING将表头映射到目标字段VALIDATION再按 Excel 坐标选择需要检查的位置。使用者看到的模板、测试人员检查的规则、开发人员加载的 XML围绕的是同一份配置契约。校验应保持责任边界。单元格格式、必填、长度、日期范围、数值范围、邮箱、手机和字典成员关系适合在VALIDATION中描述。工号是否已存在、当前操作者是否有权限、关联组织是否存在、同一文件是否存在跨行冲突以及落库事务则不应被误认为单元格规则可以替代。导入通过后拿到ListJQuickRow业务层仍需要继续执行系统一致性检查。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 坐标如下。dependencygroupIdio.github.paohaijiao/groupIdartifactIdjquick-excel/artifactIdversion3.6.0/version/dependencyXML 文件应放在类路径中。README-CN 的示例使用excels的namespace指定服务接口并通过excel name...将 DSL 规则绑定到接口方法。CDATA 可避免 XML 解析器把 DSL 内的花括号、引号和${...}当作普通 XML 内容处理。?xml version1.0 encodingUTF-8?!DOCTYPEexcelsPUBLIC-//PAOHAIJIAO//DTD API EXCEL 1.0//ENclasspath:paohaijiao/dtd/Jquick-excel.dtdexcelsnamespacecom.example.excel.EmployeeExcelServiceexcelnameimportEmployeesreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET员工导入, HEADERtrue, 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(InputStreaminputgetClass().getClassLoader().getResourceAsStream(employees.xlsx)){JContextcontextnewJContext();JQuickParseHandlerparsernewJQuickExcelImportXmlParseFactory(context,input);JQuickFactoryfactorynewJQuickXmlFactory(parser,jquick-excel.xml);EmployeeExcelServiceservicefactory.createApi(EmployeeExcelService.class);ListJQuickRowrowsservice.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 行开始是因为第一行被HEADERtrue作为表头处理。excelnameimportEmployeesreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET员工导入, HEADERtrue, 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使用minValuemax_value使用maxValuemin_date使用minDatemax_date使用maxDate并同时提供format。以下示例把同一范围上的规则组合起来文案分别指出类型和边界问题。excelnameimportEmployeeLimitsreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET员工导入, HEADERtrue, 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 而强行复用约束。excelnameimportCodesreturnClassjava.util.List![CDATA[ IMPORT WITH SHEET编码表, HEADERtrue, 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}}} } ]]/excelREADME-CN 还提供了程序化解析和导入处理的基础能力。它可用于验证一段 DSL 与最小工作簿而生产工程仍可继续使用 XML 代理。下例不创造另一套规则只是把相同的IMPORT WITH字符串交给导入执行器。importcom.github.paohaijiao.statement.JQuickRow;importjava.util.List;StringruleIMPORT WITH HEADERtrue, SHEETSheet1, MAPPING{\年龄\:\age\}, VALIDATION{A2:A4:{integer{required:true,msg:年龄必须为整数}}};// 在项目实际测试中使用与当前版本匹配的导入执行器和处理器执行该规则。// 规则文本仍应保持 README-CN 所示的关键字、范围和参数拼写。ListJQuickRowrowsimportByConfiguredRule(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员工导入、HEADERtrue、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决定已映射值如何计算业务服务负责依赖外部状态的判断。这样安排后模板规则既能给用户明确反馈也不会掩盖系统层仍需承担的校验责任。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑