ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

GJSON 快速上手指南:用 Go 语言以路径语法高效查询 JSON 值

GJSON 快速上手指南:用 Go 语言以路径语法高效查询 JSON 值 序列化【免费下载链接】gjsonGet JSON values quickly - JSON parser for Go项目地址https://gitcode.com/gh_mirrors/gj/gjson点击查看免费下载GJSON 是一个面向 Go 语言的 JSON 解析库其核心目标是get json values quickly——以极简的点号路径语法从 JSON 文档中快速定位并返回任意值无需先完整反序列化为结构体或 map。本文以仓库内 README.md 为主体、以 SYNTAX.md 为路径语法补充并结合 gjson.go 源码与 gjson_test.go 测试用例系统讲解安装、路径查询、Result 类型、修饰符、JSON Lines、字节切片操作与性能特征帮助你在日志解析、配置读取、接口字段抽取等场景中直接落地使用。安装GJSON 是一个标准 Go 模块模块路径为github.com/tidwall/gjson。从 go.mod 可见它仅依赖两个小而专的库github.com/tidwall/match通配符匹配与github.com/tidwall/prettyJSON 美化输出自身保持轻量。安装方式如下$ go get -u github.com/tidwall/gjson安装完成后即可在代码中导入import github.com/tidwall/gjson提示本文所有示例均基于当前仓库源码go.mod 要求go 1.23编写实际版本能力以仓库内容为准。获取值一行代码查询 JSONGJSON 的核心 API 是gjson.Get(json, path)。它在源码中位于 gjson.go函数会对输入 JSON 做一次快速扫描遇到{进入parseObject遇到[进入parseArray按路径逐段匹配找到目标值后立即返回不会解析整个文档。package main import github.com/tidwall/gjson const json {name:{first:Janet,last:Prichard},age:47} func main() { value : gjson.Get(json, name.last) println(value.String()) }输出Prichard路径采用点号语法如name.last或age。从源码结构看Get内部会优先处理修饰符开头、JSON 字面量!开头和多路径[/{开头等特殊语法然后才进入常规的对象/数组逐层匹配这也是它支持丰富路径语法的底层基础。路径语法完整路径语法说明见 SYNTAX.md。一个 GJSON 路径本质上是用.分隔的一系列组件除.外|、#、、\、*、!、?也具有特殊含义。下面以 README 与 SYNTAX 文档共用的一组 JSON 作为示例{ 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]} ] }基本查询按对象键名或数组下标取值是最常见的使用方式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通配符键中可以包含通配符*匹配任意 0 个及以上字符和?匹配恰好 1 个字符child*.2 Jack c?ildren.0 Sara通配符的匹配语义由依赖库github.com/tidwall/match提供支持在路径中间任意位置使用。转义字符.、*、?等特殊字符可用\转义。比如要查询键名为fav.movie的字段fav\.movie Deer Hunter注意在 Go 源码中硬编码路径时普通字符串字面量里的\本身需要转义而使用反引号原始字符串则无需二次转义SYNTAX.mdval : gjson.Get(json, fav\\.movie) // 普通字符串必须转义反斜杠 val : gjson.Get(json, fav\.movie) // 反引号原始字符串无需转义数组#字符#用于深入数组。单独使用可获取数组长度与子路径组合可对每个元素逐一遍历取值children.# 3 friends.# 3 friends.#.age [44,68,47]friends.#.age等价于对 friends 数组中每个元素取 age 字段返回一个数组。查询#(...)可以在数组中查询第一个匹配项#(...)或查询全部匹配项#(...)#。查询支持比较运算符、!、、、、以及模式匹配运算符%like类似和!%not like不类似friends.#(last\Murphy\).first Dale friends.#(last\Murphy\)#.first [Dale,Jane] friends.#(age45)#.last [Craig,Murphy] friends.#(first%D*).last Murphy friends.#(first!%D*).last Craig对数组中的非对象值可以省略运算符右侧的字符串SYNTAX.mdchildren.#(!%*a*) Alex children.#(%*a*)# [Sara,Jack]查询还支持嵌套例如friends 中 nets 数组包含 fb 的项friends.#(nets.#(\fb\))#.first [Dale,Roger]历史兼容性说明在 v1.3.0 之前查询使用#[...]方括号语法v1.3.0 后为避免与新引入的多路径multipath语法混淆而改为#(...)。为向后兼容#[...]会继续工作到下一个大版本。波浪号~运算符布尔转换查询~波浪号运算符会在比较前将值转换为布尔语义SYNTAX.md支持以下转换类型~true 将真值转换为 true ~false 将假值和不存在的值转换为 true ~null 将 null 和不存在的值转换为 true ~* 将任意存在的值转换为 true例如对如下 JSON{ 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 } ] }可以查询所有真值或假值vals.#(b~true)#.a [2,6,7,8] vals.#(b~false)#.a [3,4,5,9,10,11]其中不存在的值被当作false处理。查询 null 与显式存在性vals.#(b~null)#.a [10,11] vals.#(b~*)#.a [1,2,3,4,5,6,7,8,9,10] vals.#(b!~*)#.a [11]点号 vs 管道符.是标准分隔符|在大多数情况下返回相同结果。两者的关键差异出现在#数组/查询之后.后缀会在返回前对数组的每个元素先处理子路径而|后缀是对上一步整体结果再处理子路径SYNTAX.md。friends.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)#本身的结果是包含两个元素的数组后缀.first会在返回前对每个元素取first字段得到[Dale,Jane]而后缀|first是在返回后对整体一个数组执行first路径——数组上不存在first字段因此结果是不存在但|0是对数组取下标 0因此返回第一个匹配对象同理|#得到数组长度 2而.#由于是在返回前对元素逐个取#元素是对象不是数组所以得到空数组。多路径Multipath拼接新文档自 v1.3.0 起GJSON 支持把多个路径拼接成新文档用[...]包裹逗号分隔的路径得到新数组用{...}得到新对象SYNTAX.md。例如{name.first,age,the_murphys:friends.#(lastMurphy)#.first}该路径选取了 first 名、age以及所有 last 为 Murphy 的朋友的 first 名。这里为第三个值显式指定了键名the_murphys若不指定则沿用实际字段名如first无法确定字段名时使用_。执行结果为{first:Tom,age:37,the_murphys:[Dale,Jane]}从源码看多路径由 gjson.go 中的子选择器subSelector解析逻辑实现Get检测到路径以[或{开头时会解析出各子路径并对每个子路径分别执行Get再把结果拼接为新的 JSON 数组或对象。JSON 字面量Literals构造静态 JSON 块自 v1.12.0 起GJSON 支持 JSON 字面量语法以!声明字符开头用于在路径中直接构造静态 JSON 块特别适合配合多路径构造新文档SYNTAX.md。{name.first,age,company:!Happysoft,employed:!true}结果为{first:Tom,age:37,company:Happysoft,employed:true}Get源码中同样处理了以!开头的路径gjson.go走execStatic逻辑。Result 类型GJSON 支持 JSON 的string、number、bool、null类型数组与对象以原始 JSON 返回。所有查询结果统一封装为Result结构体其定义位于 gjson.gotype Result struct { Type Type // 值类型 Raw string // 原始 JSON Str string // JSON 字符串值 Num float64 // JSON 数值 Index int // 原始值在源 JSON 中的偏移0 表示未知 Indexes []int // 路径含 # 查询字符时所有匹配元素的偏移 }Type枚举gjson.go取值为Null、False、Number、String、True、JSON原始 JSON 块。各字段对应关系result.Type // String, Number, True, False, Null 或 JSON result.Str // 字符串值 result.Num // float64 数值 result.Raw // 原始 JSON 文本 result.Index // 原始值在源 JSON 中的偏移0 表示未知 result.Indexes // 路径含 # 查询字符时所有匹配元素的偏移列表Result提供一系列便捷转换方法见 README 与 gjson.go 中对应实现result.Exists() bool // 值是否存在源码 gjson.go#L658 result.Value() interface{} // 转为 interface{}需类型断言 result.Int() int64 result.Uint() uint64 result.Float() float64 result.String() string result.Bool() bool result.Time() time.Time // 按 RFC3339 解析时间 result.Array() []gjson.Result result.Map() map[string]gjson.Result result.Get(path string) Result // 在结果上继续查询 result.ForEach(iterator func(key, value Result) bool) result.Less(token Result, caseSensitive bool) boolresult.Value()返回的 Go 类型与 JSON 类型对应关系如下boolean bool number float64 string string null nil array []interface{} object map[string]interface{}result.Array()返回数组值若结果不存在则返回空数组若结果本身不是 JSON 数组则返回只包含一个元素的数组。64 位整数支持result.Int()与result.Uint()能读取完整的 64 位整数支持大整数 JSON 值result.Int() int64 // -9223372036854775808 到 9223372036854775807 result.Uint() uint64 // 0 到 18446744073709551615从 gjson.go 的实现可以看到Int()/Uint()会先尝试把Numfloat64安全转换失败后再回退解析Raw原始字符串从而保证超出 float64 精确表示范围的 64 位整数也能被正确读取。修饰符与路径链自 v1.2 起GJSON 支持修饰符函数与路径链修饰符是对 JSON 执行自定义处理的路径组件多个路径可以用管道符|链接从修饰后的结果中继续查询。例如对children数组应用内置的reverse修饰符反转顺序children|reverse [Jack,Alex,Sara] children|reverse|0 Jack内置修饰符完整清单如下reverse反转数组或对象成员顺序。ugly移除 JSON 文档中所有空白。pretty让 JSON 文档更易读。this返回当前元素可用于取回根元素。valid校验 JSON 文档是否合法。flatten扁平化数组。join将多个对象合并为一个对象。keys返回对象的所有键构成的数组。values返回对象的所有值构成的数组。tostr将 JSON 转为字符串包装成一个 JSON 字符串。fromstr从 JSON 字符串中还原解包 JSON 字符串。group对对象数组分组。dig无需提供完整路径即可搜索某个值。修饰符参数修饰符可接受可选参数参数可以是合法 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其美化能力由依赖库github.com/tidwall/pretty提供。自定义修饰符通过gjson.AddModifier(name, fn)源码位于 gjson.go可以注册自定义修饰符。函数签名为func(json, arg string) string其中json是当前元素arg是修饰符参数。例如注册一个把整个 JSON 转大写或小写的修饰符gjson.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]说明README 与 SYNTAX 中同时存在case修饰符通过.或|链接的写法如children.case:lower.reverse两种路径链风格均可使用语义差异遵循点号 vs 管道符一节所述的规则。另外源码中提供全局开关DisableModifiersgjson.go将其置为true可禁用修饰符语法。JSON Lines多行 JSON 文档GJSON 支持 JSON Lines 格式——用..前缀把多行文档当作一个数组处理。例如下面 4 行 JSON{name: Gilbert, age: 61} {name: Alexa, age: 34} {name: May, age: 57} {name: Deloise, age: 44}对应的路径与结果..# 4 ..1 {name: Alexa, age: 34} ..3 {name: Deloise, age: 44} ..#.name [Gilbert,Alexa,May,Deloise] ..#(name\May\).age 57从源码看Get检测到路径以..开头时会设置parseContext.lines true并进入按行解析的parseArray分支gjson.go。此外还有gjson.ForEachLine函数gjson.go逐行迭代gjson.ForEachLine(json, func(line gjson.Result) bool{ println(line.String()) return true })获取嵌套数组值假设要取出下面 JSON 中所有程序员的姓{ programmers: [ { firstName: Janet, lastName: McLaughlin, }, { firstName: Elliotte, lastName: Hunter, }, { firstName: Jason, lastName: Harold, } ] }使用路径programmers.#.lastNameresult : gjson.Get(json, programmers.#.lastName) for _, name : range result.Array() { println(name.String()) }也可以对数组内对象做条件查询name : gjson.Get(json, programmers.#(lastNameHunter).firstName) println(name.String()) // 输出 Elliotte遍历对象或数组Result.ForEach可以快速遍历对象或数组gjson.go对象迭代时向回调传入 key 与 value数组迭代时只传入 value回调返回false即停止迭代。result : gjson.Get(json, programmers) result.ForEach(func(key, value gjson.Result) bool { println(value.String()) return true // 继续迭代 })简单的 Parse 与 Get 组合gjson.Parse(json)只做一次简单解析gjson.go随后可以在Result上继续调用result.Get(path)逐级查询。以下三种写法结果等价gjson.Parse(json).Get(name).Get(last) gjson.Get(json, name).Get(last) gjson.Get(json, name.last)检查值是否存在Result.Exists()返回true表示值存在源码判断逻辑为Type ! Null || len(Raw) ! 0见 gjson.govalue : gjson.Get(json, name.last) if !value.Exists() { println(no last name) } else { println(value.String()) } // 一步完成 if gjson.Get(json, name.last).Exists() { println(has a last name) }校验 JSON 合法性Get*与Parse*系列函数假定输入是格式良好的 JSON。畸形 JSON不会导致 panic但可能返回意外结果。如果数据来自不可控来源建议先用gjson.Validgjson.go校验if !gjson.Valid(json) { return errors.New(invalid json) } value : gjson.Get(json, name.last)反序列化为 map通过result.Value()加类型断言可以拿到map[string]interface{}m, ok : gjson.Parse(json).Value().(map[string]interface{}) if !ok { // 不是 map }使用字节切片[]byte当 JSON 存放在[]byte中时应优先使用gjson.GetBytes(json, path)gjson.go避免Get(string(data), path)带来的字符串转换开销var json []byte ... result : gjson.GetBytes(json, path)如果想在GetBytes下避免把result.Raw转成[]byte可以利用result.Index字段做零分配的子切片best-effort即尽力而为var json []byte ... result : gjson.GetBytes(json, path) var raw []byte if result.Index 0 { raw json[result.Index:result.Indexlen(result.Raw)] } else { raw []byte(result.Raw) }result.Index表示原始数据在源 JSON 中的位置当它等于 0未知时退化为把result.Raw转为[]byte。从源码看常规查询结束时Get会调用fillIndex(json, c)回填索引信息gjson.go这就是该模式能工作的前提。一次查询多个路径GetMany除单路径查询外gjson.GetMany/gjson.GetManyBytesgjson.go支持对同一份 JSON 批量查询多个路径返回的[]Result长度与传入路径数量一致func GetMany(json string, path ...string) []Result func GetManyBytes(json []byte, path ...string) []Result适合需要同时抽取多个字段、又不想多次扫描文档的场景。性能特征README 中记录了 GJSON 与 Go 标准库encoding/json、ffjson、EasyJSON、jsonparser、json-iterator等库的对比基准该基准运行于 MacBook Pro M1 Max、Go 1.22 环境具体数据见 README.mdBenchmarkGJSONGet-10 17893731 202.1 ns/op 0 B/op 0 allocs/op BenchmarkGJSONUnmarshalMap-10 1663548 2157 ns/op 1920 B/op 26 allocs/op BenchmarkJSONUnmarshalMap-10 832236 4279 ns/op 2920 B/op 68 allocs/op BenchmarkJSONUnmarshalStruct-10 1076475 3219 ns/op 920 B/op 12 allocs/op BenchmarkJSONDecoder-10 585729 6126 ns/op 3845 B/op 160 allocs/op BenchmarkFFJSONLexer-10 2508573 1391 ns/op 880 B/op 8 allocs/op BenchmarkEasyJSONLexer-10 3000000 537.9 ns/op 501 B/op 5 allocs/op BenchmarkJSONParserGet-10 13707510 263.9 ns/op 21 B/op 0 allocs/op BenchmarkJSONIterator-10 3000000 561.2 ns/op 693 B/op 14 allocs/op其中最突出的是GJSONGet单次路径查询约 202 ns、0 B 分配、0 次 alloc——这正是 GJSON按需取值、跳过无关内容的设计带来的优势。基准使用的 JSON 文档widget 结构与轮换的查询路径widget.window.name、widget.image.hOffset、widget.text.onMouseUp见 README.md。需要注意的是基准数据仅代表其声明的硬件/Go 版本环境下的结果实际性能应结合自身数据规模与运行环境复测若追求完整反序列化到结构体或 map 的场景encoding/json仍是标准选择GJSON 的核心价值在于只取所需。小结从本文可以看出GJSON 以一套简洁的路径语法覆盖了绝大多数 JSON 取值场景点号路径与通配符应对常规查询#处理数组长度与元素遍历#(...)实现条件查询与~布尔语义转换|与修饰符完成结果变换与链式处理..适配 JSON Lines多路径与 JSON 字面量还能直接构造新文档配合Result的字段与方法体系、字节切片零分配模式以及按需解析的性能设计它非常适合作为日志字段抽取、接口响应裁剪、配置读取等高频读取场景的基础设施。更完整的语法细节可继续查阅 SYNTAX.md源码级实现与测试用例可分别在 gjson.go 与 gjson_test.go 中研读。赞分享序列化【免费下载链接】gjsonGet JSON values quickly - JSON parser for Go项目地址https://gitcode.com/gh_mirrors/gj/gjson点击查看免费下载相关推荐Sliver 项目内置 GJSON 路径语法实战指南快速检索 JSON 载荷的查询语言Sliver 项目内置 GJSON 路径语法实战指南快速检索 JSON 载荷的查询语言 导读 GJSON Path 是一种用于从 JSON 载荷中快速检索值的网络安全GJSON路径语法完全指南快速掌握JSON查询的终极技巧GJSON路径语法完全指南快速掌握JSON查询的终极技巧 GJSON是一款专为Go语言设计的高性能JSON解析工具它提供了简单直观的路径语法让开发者能够快序列化深入解析GJSON路径语法高效查询JSON数据的利器深入解析GJSON路径语法高效查询JSON数据的利器 GJSON是一个强大的Go语言JSON解析库其核心特性之一就是提供了一套简洁高效的路径查询语法。本文将序列化上一篇终极指南如何快速为Photoshop安装AVIF插件实现高效图像处理下一篇图片秒变文字Qwen2.5-VL-7B-Instruct-unsloth-bnb-4bit图像理解实战OCR、图表与多图推理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表