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 的人,则可以: