原文地址:https://goessner.net/articles/JsonPath/
XML 的一个经常被强调的优点是有大量工具可用于分析、转换和有选择性地从 XML 文档中提取数据。XPath 就是这些强大的工具之一。
现在我们应该想一想,是否需要 XPath4JSON 这样的工具,它能解决哪些问题。
可以在客户端上交互式地查找数据并从 JSON 结构中提取数据,而无需编写特殊脚本。
可将客户端请求的 JSON 数据缩减为服务器上的相关部分,从而最大限度地减少服务器响应的带宽使用。
如果我们同意从手头的 JSON 结构中提取部分内容的工具确实有意义,那么就会出现一些问题。它应该如何工作?JSONPath 表达式是怎样的?
对于 C 系列编程语言来说,JSON 是一种自然的数据表示形式,因此特定语言很有可能拥有访问 JSON 结构的本地语法元素。
下面的 XPath 表达式:
/store/book[1]/title
会看起来像
x.store.book[0].title
或者
x['store']['book'][0]['title']
在 Javascript、Python 和 PHP 中,变量 x 包含 JSON 结构。在此我们注意到,特定语言通常已内置了基本的 XPath 功能。
有关的 JSONPath 工具应...
自然地基于这些语言特点。
只涵盖 XPath 1.0 的基本部分。
在代码大小和内存消耗方面是轻量级的。
运行时高效。
JSONPath 表达式总是引用 JSON 结构,就像 XPath 表达式与 XML 文档结合使用一样。由于 JSON 结构通常是匿名的,不一定有一个 "根成员对象",因此 JSONPath 假设抽象名称 $ 分配给外层对象。
JSONPath 表达式可以使用点符号:
$.store.book[0].title
或括号注释
$['store']['book'][0]['title']
用于输入路径,内部或输出路径将始终转换为更通用的括号符号。
JSONPath 允许在成员名称和数组索引中使用通配符符号 *。它借用了 E4X 中的后裔运算符 "..." 和 ECMASCRIPT4 中的数组切片语法建议 [start:end:step]。
底层脚本语言的表达式(<expr>)可以用来替代明确的名称或索引,例如:
$.store.book[(@.length-1)].title
使用符号 "@" 表示当前对象。过滤表达式通过语法 ?(<boolean expr>) 支持,例如:
$.store.book[?(@.price < 10)].title
下面是 JSONPath 语法元素与 XPath 对应元素的完整概述和并排比较:
XPath | JSONPath | 描述 |
/ | $ | 根对象/元素 |
. | @ | 当前对象/元素 |
/ | . or [] | 子操作符 |
.. | n/a | 父操作符 |
// | .. | 递归解析,JSONPath 从 E4X 中借鉴了这种语法。 |
* | * | 通配符。所有对象/元素,无论其名称如何。 |
@ | n/a | 属性访问。JSON 结构没有属性。 |
[] | [] | 下标操作符。XPath 使用它来遍历元素集合和谓词。在 Javascript 和 JSON 中,它是本地数组运算符。 |
| | [,] | XPath 中的联合运算符会导致节点集的组合。JSONPath 允许将备用名称或数组索引作为一个集合。 |
n/a | [start:end:step] | 从 ES4 中借用的数组切片操作符。 |
[] | ?() | 应用过滤器(脚本)表达式。 |
n/a | () | 脚本表达式,使用底层脚本引擎。 |
() | n/a | 在 Xpath 中分组 |
XPath 提供的功能(非缩写语法的位置路径、运算符和函数)比这里列出的要多得多。此外,下标操作符在 Xpath 和 JSONPath 中的工作方式也有显著不同。
XPath 表达式中的方括号总是对上一个路径片段产生的节点集进行操作,索引总是以 1 开始。
JSONPath 中的方括号对前一个路径片段寻址的对象或数组执行操作,索引总是以 0 开始。
让我们通过更多示例来练习 JSONPath 表达式。我们从一个简单的 JSON 结构开始,该结构是根据表示书店的 XML 示例(原始 XML 文件)构建的。
{ "store": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } }
XPath | JSONPath | 结果 |
/store/book/author | $.store.book[*].author | 获取商店中所有书籍的作者 |
//author | $..author | 获取所有作者 |
/store/* | $.store.* | 店里的所有东西,包括几本书和一辆红色自行车。 |
/store//price | $.store..price | 店里所有东西的价格。 |
//book[3] | $..book[2] | 第三本 |
//book[last()] | $..book[(@.length-1)] | 最后一本书的顺序。 |
//book[position()<3] | $..book[0,1] | 前两本书 |
//book[isbn] | $..book[?(@.isbn)] | 获取所有拥有 isbn 的书籍 |
//book[price<10] | $..book[?(@.price<10)] | 过滤所有低于 10 美元的书籍 |
//* | $..* | XML 文档中的所有元素。JSON 结构的所有成员。 |
JSONPath 在 Javascript 中实现,供客户端使用,并移植到 PHP 中供服务器使用。
您只需下载以下任一文件
将其包含在你的程序中,并使用由一个函数组成的简单应用程序接口:
jsonPath(obj, expr [, args])
参数:
obj (object|array) 代表 JSON 结构的对象。
expr (string) JSONPath 表达式字符串。
args (object|undefined) 控制路径评估和输出的对象。目前只支持一个成员。
args.resultType ("VALUE"|"PATH") 会使结果变成匹配值(默认值)或规范化路径表达式。
返回值:
(array|false) 数组,包含与输入路径表达式匹配的值或规范化路径表达式,可用于懒惰评估。
Javascript 示例:
var o = { /*...*/ }, // the 'store' JSON object from above res1 = jsonPath(o, "$..author").toJSONString(), res2 = jsonPath(o, "$..author", {resultType:"PATH"}).toJSONString();
PHP 示例:
我们需要先将 JSON 字符串转换为 PHP 数组。为此,我使用了 Michal Migurski 的 JSON 解析器。例如:
require_once('json.php'); // JSON parser require_once('jsonpath.php'); // JSONPath evaluator $json = '{ ... }'; // JSON structure from above $parser = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); $o = $parser->decode($json); $match1 = jsonPath($o, "$..author"); $match2 = jsonPath($o, "$..author", array("resultType" => "PATH")); $res1 = $parser->encode($match1); $res2 = $parser->encode($match2);
结果:
Javascript 和 PHP 示例都会产生以下 JSON 数组(字符串):
res1: [ "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien" ] res2: [ "$['store']['book'][0]['author']", "$['store']['book'][1]['author']", "$['store']['book'][2]['author']", "$['store']['book'][3]['author']" ]
请注意,jsonPath 的返回值是一个数组,它也是一个有效的 JSON 结构。因此,您可能需要再次将 jsonPath 应用于生成的结构,或者使用您最喜欢的数组方法对其进行排序。
目前 JSONPath 表达式中只允许使用单引号。
jsonPath 目前不会递归评估 JSONPath 位置内的脚本表达式。只有全局的 $ 和局部的 @ 符号会通过简单的正则表达式展开。
jsonPath 在没有匹配结果的情况下返回 false 的另一种方法是将来返回一个空数组。