函数扩展
什么是函数扩展?
函数扩展(Function Extension) 是 JSONPath 提供的内置函数,可以在过滤器表达式中调用,实现更强大的查询能力。
RFC 9535 定义了 5 个标准函数:
| 函数 | 作用 | 参数类型 | 结果类型 |
|---|---|---|---|
length() | 计算长度 | ValueType | ValueType(数字) |
count() | 计算节点数量 | NodesType | ValueType(数字) |
match() | 正则完全匹配 | ValueType, ValueType | LogicalType |
search() | 正则子串匹配 | ValueType, ValueType | LogicalType |
value() | 提取单个节点的值 | NodesType | ValueType |
length() 函数
length() 计算字符串长度、数组元素个数或对象成员个数。
示例 JSON
{
"employees": [
{"name": "航仔", "hobbies": ["钓鱼", "看科幻小说", "深夜撸串"]},
{"name": "翼王", "hobbies": ["健身", "改装车"]},
{"name": "图妹", "hobbies": ["手账"]},
{"name": "鸣哥", "hobbies": []}
],
"slogan": "愿你朝华相顾,愿你前程似锦。"
}
length() 查询
| 查询目标 | JSONPath | 结果 | 说明 |
|---|---|---|---|
| 标语长度 | $[?length(@.slogan) > 10] | 整个 JSON | 标语长度>10 |
| 爱好>=3个的员工 | $.employees[?length(@.hobbies) >= 3] | 航仔 | 航仔有3个爱好 |
| 爱好=2个的员工 | $.employees[?length(@.hobbies) == 2] | 翼王 | 翼王有2个爱好 |
| 爱好=0个的员工 | $.employees[?length(@.hobbies) == 0] | 鸣哥 | 鸣哥没有爱好 |
💡
length(@.hobbies)对数组返回元素个数,对字符串返回字符数,对对象返回成员数。
count() 函数
count() 计算节点列表中有多少个节点。
示例 JSON
{
"departments": [
{
"name": "技术部",
"members": [{"name": "航仔"}, {"name": "翼王"}, {"name": "凌叔"}]
},
{
"name": "产品部",
"members": [{"name": "图妹"}, {"name": "星宇"}]
},
{
"name": "运营部",
"members": [{"name": "鸣哥"}, {"name": "雁姐"}, {"name": "波比"}, {"name": "云吞"}]
}
]
}
count() 查询
| 查询目标 | JSONPath | 结果 | 说明 |
|---|---|---|---|
| 成员>=3的部门 | $.departments[?count(@.members) >= 3] | 技术部、运营部 | 技术部3人,运营部4人 |
| 成员=2的部门 | $.departments[?count(@.members) == 2] | 产品部 | 产品部2人 |
| 成员>5的部门 | $.departments[?count(@.members) > 5] | (空) | 没有部门超过5人 |
💡
count(@.members)返回 members 数组里的节点数量。注意:count 的参数必须是 NodesType(节点列表),不能直接传数字或字符串。
match() 函数
match() 用正则表达式检查字符串是否完全匹配。
示例 JSON
{
"employees": [
{"name": "航仔", "phone": "13800138000"},
{"name": "翼王", "phone": "13900139000"},
{"name": "图妹", "phone": "13700137000"},
{"name": "鸣哥", "phone": "020-88888888"}
]
}
match() 查询
| 查询目标 | JSONPath | 结果 | 说明 |
|---|---|---|---|
| 手机号以 138 开头 | $.employees[?match(@.phone, '138.*')] | 航仔 | 138 开头 |
| 手机号以 139 开头 | $.employees[?match(@.phone, '139.*')] | 翼王 | 139 开头 |
| 固话(带横线) | $.employees[?match(@.phone, '.*-.*')] | 鸣哥 | 包含横线 |
💡
match()是"完全匹配",字符串必须整体符合正则表达式。正则语法遵循 I-Regexp(RFC 9485)。
search() 函数
search() 用正则表达式检查字符串中是否包含匹配的子串(不需要完全匹配)。
search() vs match()
| 函数 | 匹配方式 | 示例 |
|---|---|---|
match() | 完全匹配 | match("abc", "a.*") → true |
search() | 子串搜索 | search("abc", "b") → true |
示例 JSON
{
"employees": [
{"name": "航仔", "personality": "沉稳靠谱,话不多但代码干净"},
{"name": "翼王", "personality": "霸气外露,技术独裁但护犊子"},
{"name": "图妹", "personality": "急性子,原型图画到半夜"},
{"name": "鸣哥", "personality": "社牛,公司团建气氛组"}
]
}
search() 查询
| 查询目标 | JSONPath | 结果 | 说明 |
|---|---|---|---|
| 性格描述含"技术" | $.employees[?search(@.personality, '技术')] | 翼王 | 翼王描述中有"技术" |
| 性格描述含"牛" | $.employees[?search(@.personality, '牛')] | 鸣哥 | 鸣哥是"社牛" |
| 性格描述含"半夜" | $.employees[?search(@.personality, '半夜')] | 图妹 | 图妹"画到半夜" |
💡
search()比match()更宽松,只要字符串里包含匹配的部分就行。
value() 函数
value() 将节点列表转换为单个值,但只有当节点列表里恰好有 1 个节点时才成功。
示例
{
"store": {
"bicycle": {"color": "red", "price": 399},
"book": {"color": "blue", "price": 29}
}
}
$[?value(@..color) == "red"]
⚠️
value()的使用场景比较特殊,日常查询中不如其他函数常用。如果节点列表为空或有多个节点,返回 Nothing。
函数使用规则
良类型检查
JSONPath 要求函数的使用必须是良类型的:
| 函数 | 参数要求 | 常见错误 |
|---|---|---|
length() | 参数必须是 ValueType | length(@.*) ❌(@.* 是 NodesType) |
count() | 参数必须是 NodesType | count(1) ❌(1 不是节点列表) |
match() | 两个参数都必须是 ValueType(字符串) | match(@.*, 'a') ❌ |
search() | 两个参数都必须是 ValueType(字符串) | 同上 |
正确 vs 错误
✅ $[?length(@.slogan) > 10] // 正确
❌ $[?length(@.*) < 3] // 错误:@.* 是节点列表
✅ $[?count(@.*) == 1] // 正确
❌ $[?count(1) == 1] // 错误:1 不是节点列表
✅ $[?match(@.phone, '138.*')] // 正确
❌ $[?match(@.*, 'a')] // 错误:@.* 是节点列表
实战:飞翔科技函数综合查询
示例 JSON
{
"departments": [
{
"name": "技术部",
"members": [
{"name": "航仔", "hobbies": ["钓鱼", "看科幻小说", "深夜撸串"], "phone": "13800138000"},
{"name": "翼王", "hobbies": ["健身", "改装车", "收藏机械键盘"], "phone": "13900139000"},
{"name": "凌叔", "hobbies": ["泡茶", "养鹦鹉"], "phone": "13700137000"},
{"name": "空少", "hobbies": ["穿搭", "探店", "拍vlog"], "phone": "13600136000"},
{"name": "风速", "hobbies": ["竞速游戏", "魔方", "收集跑鞋"], "phone": "13500135000"}
]
},
{
"name": "产品部",
"members": [
{"name": "图妹", "hobbies": ["手账", "盲盒", "奶茶测评"], "phone": "13800138111"},
{"name": "星宇", "hobbies": ["天文", "indie游戏"], "phone": "13900139222"},
{"name": "靓晴", "hobbies": ["插画", "烘焙", "逛展"], "phone": "13700137333"}
]
}
]
}
综合查询
| 查询目标 | JSONPath | 结果 |
|---|---|---|
| 爱好>=3个的员工 | $.departments[*].members[?length(@.hobbies) >= 3] | 航仔、翼王、空少、风速、图妹、靓晴 |
| 部门成员>=4人 | $.departments[?count(@.members) >= 4] | 技术部 |
| 手机号 138 开头的员工 | $.departments[*].members[?match(@.phone, '138.*')] | 航仔、图妹 |
| 爱好含"游戏"的员工 | $.departments[*].members[?search(@.hobbies, '游戏')] | 风速(竞速游戏)、星宇(indie游戏) |
一句话总结
提示
length() 算长度,count() 数节点,match() 完全正则匹配,search() 子串正则匹配。注意参数类型要对,节点列表和值不能混用!