JSON,全称 JavaScript Object Notation,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
的人,则可以: