Nhost 依赖 GJSON 路径语法详解:从 Basic 到 Multipaths 的 JSON 取值实战指南
Nhost 依赖 GJSON 路径语法详解从 Basic 到 Multipaths 的 JSON 取值实战指南【免费下载链接】nhostThe Open Source Firebase Alternative with GraphQL.项目地址: https://gitcode.com/GitHub_Trending/nh/nhost本文系统讲解 Nhost 仓库中 vendored 的 gjson 库v1.18.0所支持的路径语法GJSON Path以.分隔的组件结构、*/?通配符、\转义、#数组操作与#(...)条件查询、.与|的语义差异、内置及自定义 Modifiersreverse、pretty、dig等、v1.3.0 引入的 Multipaths 以及 v1.12.0 引入的 JSON Literals。读完本文你可以仅凭一行路径字符串从任意 JSON 负载中快速提取、过滤、重组数据并理解每种语法的适用场景与版本前提。一、GJSON 路径是什么以及它在 Nhost 仓库中的位置GJSON 路径是一种文本字符串语法用于描述搜索模式从而从 JSON 负载中快速检索值。其权威实现在github.com/tidwall/gjson包中。在 Nhost 仓库中该库以 vendored 依赖的形式存在根目录 go.mod 第 210 行声明github.com/tidwall/gjson v1.18.0 // indirect间接依赖vendor/modules.txt 第 1164 行确认其版本同为 v1.18.0核心实现位于 vendor/github.com/tidwall/gjson/gjson.go。从 vendor 目录结构看直接引用 gjson 的主要是 openai-go、anthropic-sdk-go 等第三方 SDK 内部模块。可以推断Nhost 自身服务如services/ai下的 Go 代码通过引入这些 AI SDK 间接依赖 gjson用它在解析大模型返回的 JSON 响应中做轻量取值——这正是 GJSON 路径语法最典型的应用场景不解析整个 JSON仅按路径取出需要的片段。二、路径结构Path structure一个 GJSON 路径应当能够表达为一串以.字符分隔的组件。除.之外还有若干具有特殊含义的字符字符含义.标准分隔符\|后置管道用于数组/查询结果的二次处理#数组长度与数组查询Modifier自定义处理器前缀\转义字符*通配匹配任意零个或多个字符!JSON Literal 声明字符v1.12.0?通配匹配任意单个字符全文示例 JSON下文所有路径示例均基于如下 JSON与 vendor/github.com/tidwall/gjson/SYNTAX.md 原文一致{ name: {first: Tom, last: Anderson}, age:37, children: [Sara,Alex,Jack], fav.movie: Deer Hunter, friends: [ {first: Dale, last: Murphy, age: 44, nets: [ig, fb, tw]}, {first: Roger, last: Craig, age: 68, nets: [fb, tw]}, {first: Jane, last: Murphy, age: 47, nets: [ig, tw]} ] }三、Basic按键名与数组下标取值最常见的需求就是按对象名或数组下标直接取值name.last Anderson name.first Tom age 37 children [Sara,Alex,Jack] children.0 Sara children.1 Alex friends.1 {first: Roger, last: Craig, age: 68} friends.1.first Roger注意两点取数组本身children返回的是完整 JSON 数组文本取children.0才得到单元素取对象元素friends.1返回的是该元素的完整 JSON 对象继续用.拼接即可深入字段。四、Wildcards*与?通配键名键名中可以使用通配符*匹配任意零个或多个字符?匹配任意一个字符。child*.2 Jack c?ildren.0 Sara这在键名不完全确定例如带数字后缀、带随机前缀的负载中特别有用。五、Escape character用\转义特殊字符当键名本身包含.、*、?等字符时例如示例中的fav.movie需要用\转义fav\.movie Deer Hunter在源码中硬编码路径时还必须注意语言层面的字符串转义// Go val : gjson.Get(json, fav\\.movie) // 双引号字符串必须转义反斜杠 val : gjson.Get(json, fav\.movie) // 反引号原始字符串无需再转义// Rust let val gjson::get(json, fav\\.movie) // 双引号字符串必须转义反斜杠 let val gjson::get(json, r#fav\.movie#) // 原始字符串无需再转义六、Arrays#求数组长度#字符允许深入 JSON 数组。单独使用#即返回数组长度与字段名组合#.age则提取每个元素的该字段组成新数组friends.# 3 friends.#.age [44,68,47]七、Queries#(...)条件查询#(...)查询数组中的第一个匹配元素#(...)#尾部再加#则找出所有匹配项。支持比较运算符、!、、、、以及模式匹配运算符%like与!%not likefriends.#(lastMurphy).first Dale friends.#(lastMurphy)#.first [Dale,Jane] friends.#(age45)#.last [Craig,Murphy] friends.#(first%D*).last Murphy friends.#(first!%D*).last Craig对非对象值的数组可以省略运算符右侧的字符串直接对元素本身做模式匹配children.#(!%*a*) Alex children.#(%*a*)# [Sara,Jack]嵌套查询查询内部还可以再嵌套查询。例如筛选出nets中包含fb的所有朋友再取他们的firstfriends.#(nets.#(fb))#.first [Dale,Roger]版本注意v1.3.0 之前查询使用#[...]方括号写法v1.3.0 为避免与 Multipaths 语法混淆而改为#(...)。为向后兼容#[...]将一直可用至下一个大版本。本仓库 vendored 的是 v1.18.0两种写法目前都有效。~tilde布尔转换运算符~运算符先将值转换为布尔再比较支持的写法~true 将类真值转换为 true ~false 将类假值及不存在值转换为 true ~null 将 null 及不存在值转换为 true ~* 将任何已存在的值转换为 true用如下 JSON 演示各元素的b字段类型刻意混杂数字、布尔、字符串、null、缺失{ vals: [ { a: 1, b: data }, { a: 2, b: true }, { a: 3, b: false }, { a: 4, b: 0 }, { a: 5, b: 0 }, { a: 6, b: 1 }, { a: 7, b: 1 }, { a: 8, b: true }, { a: 9, b: false }, { a: 10, b: null }, { a: 11 } ] }筛选所有类真 / 类假的值注意最后一个元素b缺失被视为falsevals.#(b~true)#.a [2,6,7,8] vals.#(b~false)#.a [3,4,5,9,10,11]筛选 null 及显式存在性vals.#(b~null)#.a [10,11] vals.#(b~*)#.a [1,2,3,4,5,6,7,8,9,10] vals.#(b!~*)#.a [11]八、Dot vs Pipe.与|的语义差异.是标准分隔符但大多数情况下也可以写成|。两者结果相同唯一差异出现在#数组与#(...)查询之后用.field结尾在返回结果之前对数组中的每个元素分别执行后续路径再合并用|field结尾先把前面的结果当作整体在其上执行后续路径。示例对比基于第二节的 JSONfriends.0.first Dale friends|0.first Dale friends.0|first Dale friends|0|first Dale friends|# 3 friends.# 3 friends.#(lastMurphy)# [{first: Dale, last: Murphy, age: 44},{first: Jane, last: Murphy, age: 47}] friends.#(lastMurphy)#.first [Dale,Jane] friends.#(lastMurphy)#|first non-existent friends.#(lastMurphy)#.0 [] friends.#(lastMurphy)#|0 {first: Dale, last: Murphy, age: 44} friends.#(lastMurphy)#.# [] friends.#(lastMurphy)#|# 2逐条拆解几个关键路径单独写friends.#(lastMurphy)#结果是两个 Murphy 的完整对象数组[{first: Dale, last: Murphy, age: 44},{first: Jane, last: Murphy, age: 47}]后缀.first会在返回前对每个元素处理first路径得到[Dale,Jane]后缀|first则是在上一步结果上处理first。上一步结果是一个数组而非对象数组上不存在first键因此结果为non-existent。但|0能工作0是上一步结果数组的第一个下标于是返回第一个对象{first: Dale, last: Murphy, age: 44}简记. 逐元素映射后再取| 对上一步的整体结果取。九、Modifiers内置处理器Modifier 是对 JSON 执行自定义处理的路径组件。例如内置的reverse反转数组children.reverse [Jack,Alex,Sara] children.reverse.0 Jack当前内置 Modifiers 一览Modifier作用reverse反转数组或对象的成员顺序ugly移除 JSON 中所有空白pretty美化 JSON 输出使其更易读this返回当前元素可用于取根元素valid校验 JSON 文档是否合法flatten扁平化数组join将多个对象合并为单个对象keys返回对象所有键组成的数组values返回对象所有值组成的数组tostr将 JSON 转换为字符串对字符串再包一层引号fromstr将字符串还原为 JSON去掉外层字符串包裹group对对象数组按条件分组dig无需给出完整路径即可搜索值Modifier 参数Modifier 可以接受可选参数参数可以是合法的 JSON也可以只是普通字符。例如pretty接受一个 JSON 对象参数pretty:{sortKeys:true}它会把 JSON 美化并对所有键排序输出如{ age:37, children: [Sara,Alex,Jack], fav.movie: Deer Hunter, friends: [ {age: 44, first: Dale, last: Murphy}, {age: 68, first: Roger, last: Craig}, {age: 47, first: Jane, last: Murphy} ], name: {first: Tom, last: Anderson} }pretty的完整选项为sortKeys、indent、prefix、width基于 tidwall/pretty 的定制化输出能力。自定义 ModifierGo你可以注册自己的 Modifier。下面创建一个对整个 JSON 负载做上/下大小写转换的casegjson.AddModifier(case, func(json, arg string) string { if arg upper { return strings.ToUpper(json) } if arg lower { return strings.ToLower(json) } return json }) children.case:upper [SARA,ALEX,JACK] children.case:lower.reverse [jack,alex,sara]修饰符之间可以链式串联如上例case:lower.reverse。注意Rust 版本尚不支持自定义 Modifier。十、Multipaths[...]与{...}重组文档自 v1.3.0 起GJSON 支持将多条路径组合起来构造新文档用[...]包裹逗号分隔的若干路径得到新数组用{...}得到新对象。例如{name.first,age,the_murphys:friends.#(lastMurphy)#.first}这里选取了name.first、age以及所有姓 Murphy 的朋友的first。注意可以像the_murphys:...这样提供可选的键名强制为某值指定键否则使用实际字段名本例中即first若无法确定名称则使用_。结果{first:Tom,age:37,the_murphys:[Dale,Jane]}十一、Literals!字面量v1.12.0自 v1.12.0 起GJSON 支持 JSON 字面量以!声明字符开头可在路径中直接构造静态 JSON 块。这在结合 Multipaths 组装新文档时尤为有用{name.first,age,company:!Happysoft,employed:!true}选取了name.first与age再追加两个静态字段company与employed结果为{first:Tom,age:37,company:Happysoft,employed:true}十二、实践速查与使用建议结合 Nhost 仓库中 v1.18.0 的 vendored 实现实际使用时的建议优先用.深入obj.arr.0.field是最直观的取值方式键名含特殊字符时用\转义并注意 Go 双引号字符串中\需写成\\或用反引号原始字符串规避需要逐元素映射用.需要对整体结果再处理用|——这是查询后最容易写错的地方查询时优先用#(...)圆括号语法#[...]属旧写法未来大版本可能移除组装新 JSON 用 Multipaths Literals{a,b,c:!static}一行即可完成字段挑选与静态值注入适合在 Go 服务代码里如解析 AI 响应、生成摘要文档避免引入完整 JSON 编解码器性能定位GJSON 的设计目标是不解析整份 JSON 而直接按路径扫描取值适合日志、模型响应等大体积 JSON 的轻量抽取若需要对结果做写回或深度修改仓库中同时 vendored 了配套的 sjson 可作参考。以上语法全部可在 vendor/github.com/tidwall/gjson/SYNTAX.md 中核对原文实现细节见 vendor/github.com/tidwall/gjson/gjson.go。【免费下载链接】nhostThe Open Source Firebase Alternative with GraphQL.项目地址: https://gitcode.com/GitHub_Trending/nh/nhost创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考