JQuick-Excel 导入三大配置项使用手册:MAPPING / TRANSFORM / VALIDATION
本手册专注讲解 jquick-excel 导入(
IMPORT WITH)中最核心的三个配置项:MAPPING(字段映射)、TRANSFORM(数据转换)、VALIDATION(数据校验)。所有语法与参数均基于源码校验,示例统一采用 XML 声明式写法。
目录
- 1. 总览与执行顺序
- 2. MAPPING — 字段映射
- 3. TRANSFORM — 数据转换
- 4. VALIDATION — 数据校验
- 5. 三者协同使用
- 6. 常见问题与避坑指南
- 7. 扩展机制简介
1. 总览与执行顺序
三个配置项均写在 IMPORT WITH 语句中,以逗号分隔,书写顺序任意。但在实际执行导入时,框架有固定的处理顺序:
① VALIDATION → ② MAPPING → ③ TRANSFORM
(校验原始值) (表头重命名) (值转换)
| 阶段 | 作用对象 | 说明 |
|---|---|---|
| ① VALIDATION | 原始单元格值 | 对 Excel 中尚未做任何处理的原始值进行校验,校验失败立即抛异常终止导入 |
| ② MAPPING | 表头列名 | 将 Excel 表头列名重命名为目标字段名,仅改名不改值 |
| ③ TRANSFORM | 单元格值 | 对已映射字段下的值执行转换函数,改变实际值 |
关键点: VALIDATION 校验的是原始值,TRANSFORM 转换的是校验通过后的值。若某字段需要先转换再校验,请使用 TRANSFORM 完成转换,并在程序中对转换结果做二次校验。
源码位置:JExcelImportHandler.java 的 importData 方法。
2. MAPPING — 字段映射
2.1 作用与语法
MAPPING 用于将 Excel 表头中的原始列名映射为目标字段名,只重命名,不改变值。
MAPPING = {
"Excel原始列名": "目标字段名",
...
}
- 左值:Excel 表头第 1 行中实际出现的列名(字符串,必须用引号包裹)。
- 右值:导入结果
JQuickRow中使用的字段名。 - 未在 MAPPING 中出现的列:保留原始表头列名作为字段名。
2.2 基础示例
<excel name="importMapping" returnClass="java.util.List">
<![CDATA[
IMPORT WITH
HEADER=true,
SHEET='Sheet1',
MAPPING = {
"学号": "no",
"姓名": "name",
"性别": "sex",
"年龄": "age",
"出生日期": "birthday"
}
]]>
</excel>
2.3 使用规则
| 场景 | 行为 |
|---|---|
| 列名出现在 MAPPING 左值 | 使用右值作为字段名 |
| 列名未出现在 MAPPING | 保留原始列名作为字段名 |
| MAPPING 左值在 Excel 中不存在 | 该映射被忽略(不报错) |
HEADER=false |
MAPPING 不生效,字段名按列字母(A、B、C…)生成 |
MAPPING 是 TRANSFORM 字段引用的桥梁:TRANSFORM 中的
${字段名}必须使用 MAPPING 映射后的目标字段名。
2.4 读取映射后的值
List<JQuickRow> rows = service.importExcel("1", "2");
for (JQuickRow row : rows) {
// 使用映射后的字段名取值
String no = (String) row.get("no"); // 来自"学号"列
String name = (String) row.get("name"); // 来自"姓名"列
}
3. TRANSFORM — 数据转换
3.1 作用与语法
TRANSFORM 对读取到的单元格值执行转换函数,改变实际值。转换函数基于 jquick-transform-function 提供的 226+ 内置方法,并支持 SPI 自定义扩展。
TRANSFORM = {
"字段名": 函数名(参数1, 参数2, ...)
}
- 左值:必须是 MAPPING 映射后的目标字段名。
- 右值:一个函数调用表达式。
3.2 变量与字面量
在转换函数的参数中,可使用以下几种写法:
| 写法 | 含义 | 示例 |
|---|---|---|
${字段名} |
引用当前行已映射的字段值 | ${sex} |
${变量名} |
引用 JContext 中传入的外部变量 |
${dict} |
'字符串' |
单引号包裹的字符串字面量 | 'yyyy-MM-dd' |
数字 |
数值字面量 | 1 |
布尔 |
布尔字面量 | true |
传入外部变量示例:
Map<String, Object> sexMap = new HashMap<>();
sexMap.put("男", "1");
sexMap.put("女", "2");
JContext context = new JContext();
context.put("dict", sexMap);
JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(context, inputStream);
3.3 内置转换函数
jquick-excel 默认依赖 jquick-transform-function,无需额外引入。以下为常用函数分类概览(完整列表请参考 官方仓库):
字典与翻译
| 函数 | 说明 | 示例 |
|---|---|---|
trans |
字典翻译,按 key 查 value 替换 | trans(${dict},${sex}) |
日期时间
| 函数 | 说明 | 示例 |
|---|---|---|
dateFormat |
日期格式化 | dateFormat(${birthday},'yyyy-MM-dd') |
now |
当前日期时间 | now() |
formatDate |
格式化日期 | formatDate(${date},'yyyy/MM/dd') |
parseDate |
解析日期字符串 | parseDate(${str},'yyyy-MM-dd') |
addDays / addMonths / addYears |
日期增减 | addDays(${date},7) |
daysBetween / monthsBetween / yearsBetween |
计算日期差 | daysBetween(${start},${end}) |
数学运算
| 函数 | 说明 | 示例 |
|---|---|---|
add / subtract / multiply / divide |
四则运算 | add(${age},1) |
abs / ceil / floor / round |
取整 | abs(${num}) |
max / min / avg |
统计聚合 | max(${a},${b}) |
pow / sqrt |
幂 / 根 | pow(${x},2) |
字符串
| 函数 | 说明 | 示例 |
|---|---|---|
concat |
拼接 | concat(${first},${last}) |
substring / left / right / mid |
截取 | substring(${s},0,3) |
replace / replaceAll |
替换 | replace(${s},'a','b') |
trim / toLower / toUpper |
去空格 / 大小写 | toUpper(${s}) |
mask |
掩码脱敏 | mask(${idCard},6,14,'*') |
业务方法
| 函数 | 说明 | 示例 |
|---|---|---|
idCardAge |
身份证号算年龄 | idCardAge(${idCard}) |
idCardBirthday |
身份证号提取生日 | idCardBirthday(${idCard},'yyyy-MM-dd') |
idCardGender |
身份证号取性别 | idCardGender(${idCard}) |
idCardValidate |
校验身份证号 | idCardValidate(${idCard}) |
phoneMask |
手机号脱敏 | phoneMask(${phone},3,4) |
phoneValidate |
校验手机号 | phoneValidate(${phone}) |
emailMask |
邮箱脱敏 | emailMask(${email}) |
条件与逻辑
| 函数 | 说明 | 示例 |
|---|---|---|
if |
条件判断 | if(${age}>=18,'成年','未成年') |
coalesce |
返回第一个非空值 | coalesce(${a},${b},'默认') |
defaultIfNull |
空值替换 | defaultIfNull(${remark},'无') |
eq / ne / gt / lt |
比较 | eq(${status},1) |
类型转换
| 函数 | 说明 | 示例 |
|---|---|---|
toInt / toLong / toDouble |
转数字 | toInt(${str}) |
toString / toBoolean |
转字符串 / 布尔 | toString(${num}) |
3.4 实战示例
字典翻译 + 日期格式化 + 数值运算:
<excel name="importTransform" returnClass="java.util.List">
<![CDATA[
IMPORT WITH
HEADER=true,
SHEET='Sheet1',
MAPPING = {
"学号": "no",
"姓名": "name",
"性别": "sex",
"年龄": "age",
"出生日期": "birthday",
"身份证号": "idCard",
"手机号": "phone"
},
TRANSFORM={
"sex":trans(${dict},${sex}),
"birthday":dateFormat(${birthday},'yyyy-MM-dd'),
"age":add(${age},1),
"idCard":mask(${idCard},6,14,'*'),
"phone":phoneMask(${phone},3,4)
}
]]>
</excel>
调用端传入字典:
Map<String, Object> sexMap = new HashMap<>();
sexMap.put("男", "1");
sexMap.put("女", "2");
JContext context = new JContext();
context.put("dict", sexMap);
JQuickParseHandler parser = new JQuickExcelImportXmlParseFactory(context, is);
3.5 TRANSFORM 与 MAPPING 的关系
TRANSFORM 的字段名引用必须是 MAPPING 映射后的目标字段名:
Excel 列 "性别"
↓ MAPPING
字段名 "sex"
↓ TRANSFORM
trans(${dict},${sex}) ← 这里用 ${sex},不是 ${性别}
若 MAPPING 将 "性别" 映射为 "gender",则 TRANSFORM 应写
"gender":trans(${dict},${gender})。
4. VALIDATION — 数据校验
4.1 作用与语法
VALIDATION 在导入数据前对原始单元格值进行校验,校验失败立即抛出异常终止导入。
VALIDATION = {
<目标区域>: {
<规则名>{
required: <true|false>,
msg: '<错误消息>',
map: { <参数键>: <参数值> }
},
<规则名>{ ... } // 同一区域可配多个规则,逗号分隔
},
<目标区域>: { ... }
}
重要: VALIDATION 校验的是原始值(未经 TRANSFORM 转换的值)。例如性别列在 Excel 中是 "男"/"女",则校验时也按 "男"/"女" 校验,而不是转换后的 "1"/"2"。
4.2 校验目标类型
VALIDATION 支持四种目标区域,通过不同的语法指定:
| 类型 | 语法 | 说明 | 示例 |
|---|---|---|---|
| 行 | ROW N 或 ROW N..M |
校验某行或行范围 | ROW 5、ROW 1..10 |
| 列 | COL X 或 COL X..Y |
校验某列或列范围 | COL A、COL A..D |
| 单元格 | XN |
校验单个单元格 | C2(C 列第 2 行) |
| 区域 | XN:YM |
校验矩形区域 | A1:B5 |
列也可省略
COL关键字,直接写A或A..D。
多目标校验示例:
<excel name="importMultiTarget" returnClass="java.util.List">
<![CDATA[
IMPORT WITH VALIDATION={
ROW 2..10:{
required{required:true,msg:'第2-10行不能为空'}
},
A..D:{
max_length{required:true,msg:'列长度超限',map:{maxLength:50}}
},
C2:{
regex{required:true,msg:'格式不对',map:{pattern:'^\\d+$'}}
},
A1:B5:{
min_length{required:true,msg:'长度不足',map:{minLength:2}}
}
}
]]>
</excel>
4.3 规则通用结构
每条规则由三部分组成:
| 配置项 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
required |
boolean | 是 | 是否启用校验,建议显式写 true;为 false 时跳过校验直接放行 |
msg |
string | 否 | 自定义错误消息,校验失败时抛出;不填则使用规则默认消息 |
map |
object | 视规则而定 | 规则参数,部分规则必填(见下方各规则说明) |
校验失败行为: 当 required:true 且校验不通过时,框架抛出异常(包含 msg 或默认消息),终止整个导入流程。
4.4 校验规则完整参考
以下参数键均基于源码逐一校验,
map列标注"无"表示该规则不需要map参数。
4.4.1 通用规则
| 规则名 | map 参数 | 参数类型 | 说明 | 示例 |
|---|---|---|---|---|
required |
无 | — | 必填校验,值不能为空 | required{required:true,msg:'不能为空'} |
4.4.2 字符串类规则
| 规则名 | map 参数 | 参数类型 | 说明 | 示例 |
|---|---|---|---|---|
regex |
pattern |
String | 正则匹配 | regex{required:true,msg:'只允许数字',map:{pattern:'^\\d+$'}} |
max_length |
maxLength |
数值 | 最大长度 | max_length{required:true,msg:'过长',map:{maxLength:7}} |
min_length |
minLength |
数值 | 最小长度 | min_length{required:true,msg:'过短',map:{minLength:1}} |
start_with |
startWith |
String | 必须以 X 开头 | start_with{required:true,msg:'必须以SO开头',map:{startWith:'SO'}} |
not_start_with |
notStartWith |
String | 不能以 X 开头 | not_start_with{required:true,msg:'非法前缀',map:{notStartWith:'test'}} |
end_with |
endWith |
String | 必须以 X 结尾 | end_with{required:true,msg:'必须以有限公司结尾',map:{endWith:'有限公司'}} |
not_end_with |
notEndWith |
String | 不能以 X 结尾 | not_end_with{required:true,msg:'非法后缀',map:{notEndWith:'@test.com'}} |
contain |
contains |
String | 必须包含 X | contain{required:true,msg:'必须包含关键字',map:{contains:'张三'}} |
not_contain |
notContain |
String | 不能包含 X | not_contain{required:true,msg:'含敏感词',map:{notContain:'敏感词'}} |
注意参数键命名差异: 规则名用下划线(
start_with),但参数键用驼峰(startWith)。contain的参数键是contains(带 s),not_contain的参数键是notContain(不带 s)。
4.4.3 数值类规则
| 规则名 | map 参数 | 参数类型 | 说明 | 示例 |
|---|---|---|---|---|
integer |
无 | — | 必须是整数 | integer{required:true,msg:'必须是整数'} |
decimal |
无 | — | 必须是小数 | decimal{required:true,msg:'必须是小数'} |
max_value |
maxValue |
数值 | 不超过最大值 | max_value{required:true,msg:'不能超过100',map:{maxValue:100}} |
min_value |
minValue |
数值 | 不小于最小值 | min_value{required:true,msg:'不能小于0',map:{minValue:0}} |
4.4.4 日期类规则
| 规则名 | map 参数 | 参数类型 | 说明 | 示例 |
|---|---|---|---|---|
date_format |
format |
String | 日期格式校验 | date_format{required:true,msg:'格式错误',map:{format:'yyyy-MM-dd'}} |
max_date |
format, maxDate |
String, 日期 | 不超过最大日期 | max_date{required:true,msg:'超过最大日期',map:{format:'yyyy-MM-dd',maxDate:2025-01-01}} |
min_date |
format, minDate |
String, 日期 | 不早于最小日期 | min_date{required:true,msg:'不能早于最小日期',map:{format:'yyyy-MM-dd',minDate:2022-01-01}} |
日期字面量使用
yyyy-MM-dd格式,不需要引号,如maxDate:2025-01-01。
4.4.5 其他规则
| 规则名 | map 参数 | 参数类型 | 说明 | 示例 |
|---|---|---|---|---|
email |
无 | — | 邮箱格式校验 | email{required:true,msg:'邮箱格式错误'} |
mobile |
无 | — | 中国大陆手机号(^1[3-9]\d{9}$) |
mobile{required:true,msg:'手机号格式错误'} |
dict |
键值对 | Map | 值必须存在于字典的值集合中 | dict{required:true,msg:'性别非法',map:{'1':'男','2':'女'}} |
boolean |
键值对 | Map | 值必须存在于映射的值集合中 | boolean{required:true,msg:'布尔值非法',map:{'T':'true','F':'false'}} |
composite |
无 | — | 组合规则容器(编程式扩展用) | composite{} |
dict与boolean的特殊行为: 二者都是校验"值是否在map的 values 中"。例如dict{map:{'1':'男','2':'女'}}表示 Excel 单元格的值必须是男或女(即 value),而不是1/2(即 key)。
4.5 多规则组合
同一目标区域可配置多条规则,以逗号分隔,所有规则都会执行,任一失败即抛异常:
<excel name="importMultiRule" returnClass="java.util.List">
<![CDATA[
IMPORT WITH VALIDATION={
D2:D100:{
required{required:true,msg:'年龄不能为空'},
integer{required:true,msg:'年龄必须是整数'},
min_value{required:true,msg:'年龄>=0',map:{minValue:0}},
max_value{required:true,msg:'年龄<=150',map:{maxValue:150}}
}
}
]]>
</excel>
4.6 实战示例
综合校验(字符串 + 数值 + 日期 + 字典 + 邮箱 + 手机):
<excel name="importFullValidation" returnClass="java.util.List">
<![CDATA[
IMPORT WITH
HEADER=true,
SHEET='Sheet1',
MAPPING = {
"姓名": "name",
"性别": "sex",
"年龄": "age",
"出生日期": "birthday",
"手机号": "phone",
"邮箱": "email",
"订单号": "orderId"
},
VALIDATION={
B2:B1000:{
required{required:true,msg:'姓名不能为空'},
min_length{required:true,msg:'姓名至少2位',map:{minLength:2}},
max_length{required:true,msg:'姓名最长10位',map:{maxLength:10}}
},
C2:C1000:{
dict{required:true,msg:'性别非法',map:{'1':'男','2':'女'}}
},
D2:D1000:{
integer{required:true,msg:'年龄必须是整数'},
min_value{required:true,msg:'年龄>=6',map:{minValue:6}},
max_value{required:true,msg:'年龄<=60',map:{maxValue:60}}
},
E2:E1000:{
date_format{required:true,msg:'日期格式错误',map:{format:'yyyy-MM-dd'}},
min_date{required:true,msg:'不能早于2000年',map:{format:'yyyy-MM-dd',minDate:2000-01-01}},
max_date{required:true,msg:'不能晚于2025年',map:{format:'yyyy-MM-dd',maxDate:2025-12-31}}
},
F2:F1000:{
mobile{required:true,msg:'手机号格式错误'}
},
G2:G1000:{
email{required:true,msg:'邮箱格式错误'}
},
A2:A1000:{
start_with{required:true,msg:'订单号必须以SO开头',map:{startWith:'SO'}}
}
}
]]>
</excel>
5. 三者协同使用
一个完整的导入配置通常同时使用三者:
<excel name="importStudentFull" returnClass="java.util.List">
<![CDATA[
IMPORT WITH
HEADER=true,
SHEET='学生表',
MAPPING = {
"学号": "no",
"姓名": "name",
"性别": "sex",
"年龄": "age",
"出生日期": "birthday"
},
TRANSFORM={
"sex":trans(${dict},${sex}),
"birthday":dateFormat(${birthday},'yyyy-MM-dd'),
"age":add(${age},1)
},
VALIDATION={
C2:C1000:{
dict{required:true,msg:'性别非法',map:{'1':'男','2':'女'}}
},
D2:D1000:{
integer{required:true,msg:'年龄必须是整数'},
min_value{required:true,msg:'年龄>=0',map:{minValue:0}}
},
E2:E1000:{
date_format{required:true,msg:'日期格式错误',map:{format:'yyyy-MM-dd'}}
}
}
]]>
</excel>
完整处理流程示例(以"性别"列为例):
Excel 单元格值: "男"
↓ ① VALIDATION(校验原始值 "男")
dict{map:{'1':'男','2':'女'}} → "男" 在 values 中 → ✅ 通过
↓ ② MAPPING(表头重命名)
"性别" → "sex"
↓ ③ TRANSFORM(值转换)
trans(${dict},${sex}) → 查字典 ${dict}["男"] = "1"
↓ 最终结果
JQuickRow.get("sex") = "1"
6. 常见问题与避坑指南
6.1 VALIDATION 校验的是原始值还是转换后的值?
原始值。 VALIDATION 在 TRANSFORM 之前执行,校验的是 Excel 中的原始单元格值。若 Excel 中性别列是 "男"/"女",校验时按 "男"/"女" 校验,而非转换后的 "1"/"2"。
6.2 TRANSFORM 中的字段名该写哪个?
写 MAPPING 映射后的目标字段名。若 MAPPING 将 "性别" 映射为 "sex",则 TRANSFORM 应写 "sex":trans(${dict},${sex}),而非 "性别":...。
6.3 dict / boolean 规则的 map 是按 key 还是 value 校验?
按 value 校验。dict{map:{'1':'男','2':'女'}} 表示单元格值必须是 男 或 女。
6.4 日期字面量需要加引号吗?
不需要。 maxDate:2025-01-01 直接写日期字面量,不要写成 '2025-01-01'。
6.5 required 配置项有什么作用?
required:true—— 启用该校验规则。required:false—— 跳过校验,直接放行(即使值为空或不符合规则)。
注意:这并非"字段是否必填"的语义。要校验字段非空,请使用
required规则(required{required:true,msg:'不能为空'}),而非把别的规则的required设为 true。
6.6 规则名与参数键的命名风格为什么不一致?
规则名使用下划线(如 start_with、max_length),参数键使用驼峰(如 startWith、maxLength)。这是框架的既定约定,配置时请严格对照本手册的参数表。
6.7 校验失败后会怎样?
校验失败会抛出异常(包含 msg 自定义消息或规则默认消息),终止整个导入流程,已读取的数据不会返回。若希望容错,请在调用端 try-catch 处理。
7. 扩展机制简介
7.1 自定义转换函数(TRANSFORM)
TRANSFORM 的函数基于 SPI 机制扩展,详见 jquick-transform-function。核心步骤:
- 实现
JQuickMethodFunctionProvider接口(或继承JQuickBaseFunctionFunctionProvider)。 - 在
META-INF/services/com.github.paohaijiao.function.core.JQuickMethodFunctionProvider注册实现类。 - 在 XML 的 TRANSFORM 中按
getMethodName()调用。
也支持运行时动态注册:
JQuickMethodInvocationManager manager = JQuickMethodInvocationManager.getInstance();
manager.registerInvoker("myFunc", (args) -> {
// 自定义逻辑
return result;
}, "自定义函数说明");
7.2 自定义校验规则(VALIDATION)
VALIDATION 支持自定义规则扩展:
- 继承
JAbstractValidationRule,实现doValidate(String value)和getDefaultMsg()。 - 在
JMethodValidationRuleType枚举中注册:MY_RULE("my_rule", JMyRule.class)。 - 在
JExcelValidationRuleFactory增加工厂方法。 - 在 XML 中使用:
my_rule{required:true,msg:'校验失败'}。
完整的 SPI 扩展说明请参考 useage-import.md 第 4 章。
更多用法可参考:
- README.md 使用示例章节
- useage-import.md 完整导入使用手册
- 测试用例:
src/test/java/com/github/paohaijiao/importFile/validate/