数据格式与结构化数据
从较长的 API 响应中提取所需字段
使用工具
你想把 API 响应中的商品名称放进会议笔记。搜索 name 一开始很方便,但响应里也可能有顾客的 name,或者某件商品没有该字段。手动复制就容易出错,因此需要同时指定字段及其所属位置。
下面使用虚构商品列表。JSONPath 用来选取指定位置的值,不必改写原数据。不要一开始就写复杂表达式,可以从整体、列表、单项再到名称逐步缩小范围,这样结果为空时也容易定位问题。
提供已取得的正文,而不是 URL
这不是 API 客户端,不会访问粘贴的 URL、登录或查找下一页。请准备响应正文,只输入 JSON 部分,HTTP 状态行和响应头不是 JSON。分享示例时,用同结构的虚构数据代替含真实姓名或 Token 的生产响应,更容易复现和说明。
结果针对当前输入,不是实时 API。服务器之后更新,结果不会自动刷新。若调查涉及数据新旧,应在自己的工作记录中标明检查了哪份响应,把获取来源与提取 JSON 分成两个阶段。
准备同时含商品名与负责人名的数据
{"items":[{"id":"A","name":"Notebook","price":500},{"id":"B","name":"Pencil","price":120},{"id":"C","name":null,"price":300}],"owner":{"name":"Demo team"},"page":1}打开 JSONPath 提取,把示例放进 JSON 输入框,再在独立的表达式框输入 $ 并执行,即可把整个文档选为一个结果。如果此时出错,应先检查 JSON 括号与引号。不要把表达式接在 JSON 后面粘进同一框,否则输入就不是一个 JSON 文档。
结果外面有数组,并不表示原数据被改成了数组。工具返回的是所选值的列表;选中整个文档,就得到含一个元素的结果列表。要区分结果外层数组与原数据中的 items 数组,之后遇到两层方括号就不会混淆。
先选第一项,再提取名称
$.items[0].name["Notebook"]$ 表示整个文档,items 是其下的列表,[0] 选中第一项,name 再选该商品的字段。索引从 0 而非 1 开始,改为 [1] 得到 Pencil,[2] 得到 null。每次改数字都对照原数据位置,比死记表达式更容易理解路径。
如果完整路径取不到值,可以依次测试 $.items 和 $.items[0]。字段拼写和大小写都应与原数据一致,不要在 items 和 Items 之间猜测,而应检查原文。结果为空并不意味着需要修改 JSON 中的字段名。
用 * 代替逐项复制
$.items[*].name["Notebook","Pencil",null]把 [0] 改成 [*],就会对列表各项执行相同选择。路径限定在 items 内,因此不包含 owner.name。与全文搜索 name 不同,表达式本身保留了“商品列表中的名称”这个条件,以后处理同类响应也容易说明提取范围。
结果有三项,不代表服务器总共只有三件商品。示例虽有 page 字段,工具却不会获取下一页,只能处理你提供的响应。若需要全量导出,还要另行检查原 API 的分页和条数限制。
[] 与 [null] 看似都空,却不同
$.items[2].name 得到 [null],因为第三项有 name 字段,其值为 null。$.items[3].name 得到 [],因为第四项不存在。前者选到了一个值,后者没有选到值。若都报告成“值为 null”,就无法区分明确的 null 与缺失。
{"items":[]}对这份独立示例执行 $.items,会得到 [[]],因为选中的是一个空数组值。$.items[*] 则得到 [],因为数组内部没有元素。与其数方括号,不如思考“什么被当作一个值选中”。空结果本身不代表表达式错误。
分别提取名称与价格,可能错位
{"items":[{"id":"A","name":"Notebook","price":500},{"id":"B","price":120},{"id":"C","name":"Eraser","price":300}]}提取名称得到 Notebook 和 Eraser 两项,提取价格得到 500、120、300 三项。如果把第二个名称与第二个价格横向拼接,就会误把 Eraser 的价格写成 120。不要假设缺失字段会自动补空位,结果数量不同本身就有意义。
需要保留名称与价格的关系时,可用 $.items[*] 提取完整商品对象,同时保留 id。转成表格时,应把同一对象的字段转成列,而不是按行号拼接两份独立列表。使用 JSON 与 CSV 转换时,也要确认缺失字段和 null 的表示方式。
递归搜索 name 适合调查结构
$..name回到最初的 JSON 执行此式,还会找到 Demo team。递归搜索适合寻找未知深度的同名字段,但只看结果值不容易知道来源。可以先广泛调查,确认目标层级后再缩小到 $.items[*].name。
不要对不必要的大数据使用过宽的递归搜索。工具有结果数量等限制,超过限制不代表会返回缩短但完整的结果。先在包含目标结构的小样本上验证表达式,再应用到实际输入,便于区分路径错误与处理量问题。
网上的例子不运行时,先确认支持范围
JSONPath 既有标准规范,也有实现扩展。SheepDev 不执行任意代码,并拒绝过滤与脚本表达式,因此其他网站用于筛选价格的表达式不一定能直接使用。表达式不受支持而失败,不等于符合条件的商品为零。
提取结束后,检查原数据范围、所选字段与结果数量。分享时附上表达式和范围,比单说“有两个商品名”更清楚。例如“从本页 items 中提取到两个存在的 name 值”,方便以后追查缺失或混入其他层级的问题。