JSON,全称
J
ava
S
cript
O
bject
N
otation,2001 的时候由道格拉斯 (Douglas Crockford)所发现并定义。22 岁的大学生才刚毕业,而 22 岁的 JSON 已经成为互联网数据交换的标准之一并且为广大开发者所熟知。
作为开发者之一,也算是阅 JSON 无数,JSON 有胖有瘦,有长有短。短个的 JSON 可以仅仅是仅含一个布尔值的数组:
[true]
而长的 JSON,则可以到达惊人的上万行:
在阅读长长的 JSON 的过程中,最常遇到的需求是想要提取某一个字段的值,或者遍历数组的元素对象值,而这些,都埋在了深深的 JSON 长河中。
对于一个编程新鸟,最先想到的方式是,通过编程来获取想要的字段,假设有如下结构的 JSON:
"human"
: {
"person"
: {
"man"
: [
"name"
:
"Jack"
,
"age"
:
"17"
"name"
:
"Mike"
,
"age"
:
"32"
"name"
:
"John"
,
"age"
:
"23"
"name"
:
"David"
,
"age"
:
"41"
"name"
:
"Eric"
,
"age"
:
"29"
"name"
:
"Chris"
,
"age"
:
"38"
"name"
:
"Tom"
,
"age"
:
"27"
"name"
:
"Peter"
,
"age"
:
"35"
"name"
:
"Robert"
,
"age"
:
"26"
"name"
:
"Daniel"
,
"age"
:
"33"
现在我需要获取
man
数组下所有元素中的
name
,那么我们肯定会写这边一份 JS 代码:
const nameSet = data.human.person.man.map(manItem => manItem.name)
得益于现代 JavaScript 的语法糖和 API,写出来的代码很简洁。但代码简洁,却背后还有很多问题要考虑:一是需要保存这份代码文件,二是需要运行这份代码的环境,三是需要打印或者保存执行代码的结果,四是需要不断调试这份代码和处理异常边界情况等等
编程新鸟继续思考,在 JSON 中索引值的需求场景很多,笨拙地编程似乎不是高效的方法,于是想要借助最近如火如荼的 ChatGPT:
🧑💻 我有如上的 JSON 结构,帮我取出 man 字段下所有元素的 name 字段
🤖 帮你取出 man 字段下所有元素的 name,结果如下:
Jack
Mike,
John,
David,
Eric,
Chris,
Tom,
Peter,
Robert,
Daniel
编程新鸟兴冲冲地获得了结果,很开心,但很快就遇到另外一个麻烦了,当把上万的 JSON 粘贴到 ChatGPT 输入框,很快就得到了这样的异常提示:
强如 ChatGPT 在又臭又长的文本面前也是摆烂。当然,解决方法也不是没有,分段输入,或者使用编程式调用 ChatGPT 的 API,如果是这样的话,也就回到了起点。
那么,还有没有更好的方式呢?🧐
那必须是
JSON Path
!这里我们来使用 He3 的 JSON Path 工具举例,对于上述取
name
字段集合的场景,我们只需要在输入框中输入
$..name
就可以实现:
JSON Path 工具地址:
https://t.he3app.com?c9yj
一开始看到
$..name
的时候一脸懵逼,这是什么语法?为什么可以实现?
首先,让我来介绍下 JSON Path:
JSON Path 是一种用于在 JSON 数据中定位和提取特定元素的表达式语言,提供了一种简洁的语法,使得从复杂的 JSON 结构中提取数据变得容易。
简单讲,JSON Path 同 Markdown 一样,一门轻量级的语法,能够提取 JSON 结构片段。
既然 JSON Path 是一门语法,那么有些人就会犯难了,语法就要掌握它的语法点、关键词,有学习成本在。但请相信,JSON Path 学习成本低,学会了之后,你就可以提取特定键的值、遍历数组、根据条件筛选元素、切片等等高端操作,处理 JSON 将游刃有余。
首先先介绍下 JSON Path 的一些常见语法:
$
:根节点,表示 JSON 数据的最外层。
.
:子节点操作符,用于访问对象中的属性。
[]
:索引操作符,用于访问数组中的元素或通过条件筛选元素。
*
:通配符,用于匹配任意属性名或数组索引。
..
:递归下降符号,用于搜索嵌套结构中的所有层级。
@
:当前节点,可以用于在筛选条件中引用当前节点。
假设我们现在在上诉例子 JSON 中新增了一个
"god": "God"
键值对,并且我们想要用 JSON Path 取得
god
字段,那么就可以用
$.god
:
对于
$..name
,我们就知道它表达的是”返回 JSON 数据中所有层级中具有
name
键的值“,那么如果我们要获取所有
age
,则可以改成
$..age
:
如果我们要获取
man
数组的第一个元素,则可以输入
$.human.person.man[0]
:
当然, 使用递归下降符号更加方便
$..man[0]
:
对于索引引用符
[]
,还有更多高阶操作。
比如要获取第 1、2、3 个元素,则可以输入
$..man[0,1,2]
:
对于取
1
,
2
,
3
这样连续的数组元素,JSON Path 有
[start:end]
更加方便的表达式,上诉可以改成
$..man[0:3]
:
❗️ 这里要注意一下,表达式为
[0,3
是因为取出来的元素包含
start
但不包含
end
。
JSON Path 还提供了两种表达式:
()
:
表达式
,用于进行条件判断或进行逻辑操作。可以在括号内使用比较运算符(如
>
,
<
,
==
等)和逻辑运算符(如
&&
,
||
)来定义条件。例如,
(@.length)
表示获取数组中的最后一个元素。
?()
:
过滤表达式
。在
?()
中,可以使用任意合法的 JavaScript 表达式来对元素进行筛选。这样的表达式在过滤器内部被计算,并根据其结果决定是否选择或排除当前元素。例如,
[?(@.age > 25)]
表示根据元素的 “age” 属性筛选出年龄大于 25 的元素。
有了这两个表达式,我们就可以进一步提高对数据提取的精度。比如现在要获取
man
的最后一个元素,我们可以用
$..man[(@.length-1)]
:
@
表示当前元素,
.length
表示当前元素的长度,
(@.length-1)
就能够表示获取数组最后一个元素。
如果我们想要过滤
man
字段中
age
大于等于
33
岁的人,则可以用过滤表达式
$..man[?(@. age >= 33)]
:
以上,就掌握了 JSON Path 大部分常用的语法,对于剩下的不常用,可以见:
JSON Path Plus 实现
。
JSON Path 没有一个官方的标准文档,但有一些被广泛接受和使用的实现和文档,JSON Path Plus 就是其中之一,He3 的 JSON Path 工具采用 JSON Path Plus 实现。
回归题目,在如下上万行 JSON 中:
想要查看是否有
friends
叫做
Ellen Rowland
的人,则可以: