那天翻接口日志,我盯着一整屏 JSON,心口微微一紧。想找个价格字段,手却条件反射地写起了正则。结果,光标刚跑完一遍,匹配到的东西里夹着引号、逗号,一团乱麻。桌角的水杯碰了一下,差点泼在键盘上。折腾半天,才意识到:用正则去解析一棵树,本身就是在和数据较劲。
于是换了个思路:先用 JSONPath 把需要的字段挑出来,再用正则去抠里面的细节。意外地顺畅,像走在石子路上突然换到柏油路。
两副眼镜
其实这两个工具,看数据的方式完全不同。
- JSONPath:它把 JSON 当作一棵树,用路径去找枝叶。比如
$.store.book[*].title
,直译过来就是“去 store 里的 book 数组,拿所有 title”。 - 正则:它眼里没有树,只有字符流。靠规则去匹配“形状”。
\d{11}
,就能抓到一个十一位数字。
一个关心结构,一个专注模式。
哪里容易失手
写过几次你就会发现,它们各自都有坑。
JSONPath 的问题在于兼容性。不同的库,能不能用负索引(比如 [-1:]
取最后一个元素)、支不支持复杂过滤,差别很大。换个环境,可能立马报错。
正则的麻烦则在于表达式太“晦涩”。(\+?\d{1,3}[-\s]?)?\d{11}
,一眼望去像电路图,不加注释,连自己过几天都看不懂写的是啥。写错一个量词,结果就全跑偏。
我有一次就忘了加边界符,结果匹配结果里拖进来半行日志。那一瞬间,只能盯着屏幕,让思绪打转。
哪些场景适合谁
当数据是规整的 JSON,字段和层级都清清楚楚,JSONPath 就是顺手的选择。比如找出所有价格:
1 | $..price |
意思很直白:递归搜索整个树,把所有 price 都拿出来。
但当数据只是零碎的文本片段,比如一封邮件里的验证码,根本没法解析成 JSON,这时候正则出场更快。验证码:384291
,一个 \d{6}
就够。
性能上也有差别:JSONPath 的开销在“解析 JSON”,一旦解析完,多次查询都划算;正则省掉这步,单次提取速度快,但每次都要重新扫全文。
搭配使用的三种姿势
很多时候不是非此即彼,而是结合起来用。
1. JSONPath 定位,正则清洗
比如价格字段长这样:"$10.99"
, "15.50 USD"
, "CNY 8.99"
。
先用 JSONPath 把所有 price 值取出,再在这些字符串里跑一个 /\d+(\.\d+)?/
,提取出干净的数字。
2. 正则切 JSON,JSONPath 提取
日志里,JSON 常被前后缀包住。用 \{.*?\}
先剪出 JSON 段,再交给解析器,接着用 JSONPath 去走路径。
正则在这里像剪刀,剪完交给 JSONPath 这个导航员。
3. JSONPath 拿值,正则校验
比如你只要标题里带年份的书。JSONPath:$.store.book[*].title
拿到所有标题,再用正则 /\b(19|20)\d{2}\b/
筛出有年份的。
踩过的坑
- 想用正则直接解析整份 JSON,几乎必掉坑。嵌套、转义、换行,全是麻烦。解析的活还是交给解析器。
- JSONPath 的过滤要小心类型。如果
price
存成了"10.99"
(字符串),直接写[?(@.price < 12)]
会失效。需要先转数字。 - 正则里的
.*
太贪心,一口能吞掉半份日志。用懒惰量词.*?
,或者加边界限定,才保险。 - 还有个小插曲:有次我写了个 JSONPath,在本地工具上跑得好好的,部署到线上就报错。原因是换了个实现库,根本不支持负索引。那一刻脑子空白,只能回头翻文档。
工具里的想法
如果要把两者都塞进一个工具里,我会设计成这样:
左边输入 JSONPath,把候选值挑出来;右边再输入正则,对这些值逐个清洗。结果区显示“原值 / 匹配结果”。
这样一来,结构定位和文本提取都有了,逻辑也清晰。
写到这,脑子里浮现一个小画面:JSONPath 拎着地图,带我走到目标房间;正则拿着刻刀,在细节上雕琢。
它们各自有短板,但组合起来,路就顺了。
就这样吧。