登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go GJSON 怎么一次读取多个不同路径的值

来源:17golang原创

时间:2026-10-13 15:59:34 311浏览 收藏

Go GJSON 需要读取多个不同路径时,最稳妥的做法是先用 gjson.Parse(或 ParseBytes)得到一个 Result,再通过 Result.Get 读取各个路径。这样同一份输入只建立一次解析结果;如果多个字段来自数组中的同类对象,再用 # 投影和多路径语法组合精简对象。

官方地址:https://github.com/tidwall/gjson

要点速览
  • 多个独立路径:解析一次,连续调用 root.Get(path)。
  • 同一数组的多个字段:用 items.#.field 或多路径对象收敛输出。
  • 缺失字段先看 Exists(),不要用空字符串代替业务判断。

解析一次,再读取多个独立路径

例如订单接口同时返回客户编号、订单金额和会员标记。直接对原字符串调用多次 gjson.Get 也能得到结果,但代码会把“输入文本”重复传入。把输入先交给 gjson.Parse,后续路径就集中在一个根结果上,便于统一处理缺失字段。

package main

import (
    "fmt"

    "github.com/tidwall/gjson"
)

func main() {
    payload := `{"customer":{"id":"C-17"},"order":{"total":128.5},"flags":{"vip":true}}`

    // 只解析一次,后面的 Get 都从同一个根结果继续查找。
    root := gjson.Parse(payload)
    customerID := root.Get("customer.id")
    total := root.Get("order.total")
    vip := root.Get("flags.vip")

    // 先判断存在性,再按 JSON 实际类型读取,避免空字符串掩盖缺失字段。
    fmt.Println(customerID.Exists(), customerID.String())
    fmt.Println(total.Exists(), total.Float())
    fmt.Println(vip.Exists(), vip.Bool())
}
Go GJSON 解析一次后通过 customer.id、order.total 和 flags.vip 三条路径读取不同 Result 的结构说明图
图1:Parse 一次后分发多个路径的结构说明图,不是运行截图或运行证据。

这里的“多个”是多个独立路径,不是一个叫作 GetMany 的标准 API。Result.Get 返回的仍是 gjson.Result:字符串使用 String(),数字使用 Int() 或 Float(),布尔值使用 Bool()。如果输入已经是字节切片,换成 gjson.ParseBytes(data) 即可。

数组字段用 # 做批量投影

当多个值来自数组中的同名字段,不必手写 items.0.id、items.1.id。路径中的 # 表示数组长度或遍历位置,items.#.id 会得到所有元素的 id,结果仍然是 JSON 数组。

package main

import (
    "fmt"

    "github.com/tidwall/gjson"
)

func main() {
    payload := `{"items":[{"id":101,"sku":"A-17","qty":2},{"id":102,"sku":"B-03","qty":1}]}`

    // # 遍历 items,并把每个对象的 id 投影成结果数组。
    ids := gjson.Get(payload, "items.#.id")
    for _, item := range ids.Array() {
        // 数字字段优先用 Int(),不要先转 String() 再解析。
        fmt.Println(item.Int())
    }

    // 同一输入还可以读取另一条独立路径,二者互不改变。
    quantities := gjson.Get(payload, "items.#.qty")
    fmt.Println(quantities.Raw)
}

如果要筛选数组中的多个匹配项,使用 #(条件)#;末尾第二个 # 很关键,因为没有它时通常只取第一个命中对象。筛选和投影是两个动作,先确定是否要“找哪些元素”,再决定要“留下哪些字段”。

同一对象的多个字段用多路径组装

如果调用方希望得到一个只包含指定字段的新对象数组,可以把字段映射写进多路径语法。例如 users.#.{"id":id,"name":name} 会遍历用户数组,并为每项创建只含 id 和 name 的对象。别名也可以不同,适合给下游接口生成稳定的展示结构。

package main

import (
    "fmt"

    "github.com/tidwall/gjson"
)

func main() {
    payload := `{"users":[{"id":7,"name":"Lin","role":"editor"},{"id":8,"name":"Qiao","role":"viewer"}]}`

    // 多路径只选择 id 和 name,role 等无关字段不会进入结果。
    compact := gjson.Get(payload, `users.#.{"id":id,"display_name":name}`)
    if compact.Type != gjson.JSON {
        // 类型不符合预期时及时停止向下游传递错误形态。
        panic("expected JSON array")
    }
    fmt.Println(compact.Raw)
}
GJSON 用 users.# 遍历数组并选择 id、name 形成精简对象数组的结构说明图
图2:数组投影与多路径对象组合的结构说明图,不是软件截图或执行结果。

筛选也能接在投影前面,例如 users.#(role=="editor")#. {"id":id}(实际路径中不要在点后插入空格)可以先收集所有编辑者,再只保留编号。实际项目中建议把这类路径集中成常量,避免散落在业务代码里难以修改。

缺失字段、空值与强类型转换

场景建议写法原因
判断字段是否存在result.Exists()区别于存在但值为空
保留 JSON 形态result.Raw适合直接转发数组或对象
读取整数result.Int()避免先转字符串
需要复杂校验解码到结构体GJSON 不替代领域校验

最后可以按这个边界选择方案:不同位置的少量字段,使用“解析一次 + 多次 Get”;数组同名字段,使用 #.字段;数组中每项要保留多个字段,使用多路径对象;如果后续需要必填校验、默认值和业务约束,再把精简后的 JSON 或完整输入交给结构体解码。

相关问题

多次调用 Result.Get 会重新解析整段 JSON 吗?

它会在根结果的原始 JSON 上继续查找,但代码层面复用了同一个解析结果。若路径数量很多,仍应把路径和取值逻辑集中管理,避免业务判断散落。

为什么读取不存在的路径后 String() 是空字符串?

空字符串可能来自缺失字段,也可能来自真实的空值。先调用 Exists(),必要时再检查 Type,不要只用 String() 的返回值判断。

多路径结果能直接当作 Go 结构体吗?

不能直接当作强类型结构体。多路径首先返回 gjson.Result 和 JSON 原文;需要强类型字段、必填规则或复杂校验时,仍要使用标准 JSON 解码或项目自己的转换层。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>