JSON 数据处理:JSONPath 与正则的区别与联合实战

JsonTool
订阅
充电
|

那天翻接口日志,我盯着一整屏 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 拎着地图,带我走到目标房间;正则拿着刻刀,在细节上雕琢。
它们各自有短板,但组合起来,路就顺了。

就这样吧。