
本文详解如何使用 Go 的 encoding/json 包解析嵌套 JSON 响应,安全访问结构体数组中的字段(如股票价格),并通过循环遍历提取所有值,避免 panic 和空指针错误。
本文详解如何使用 Go 的 `encoding/json` 包解析嵌套 JSON 响应,安全访问结构体数组中的字段(如股票价格),并通过循环遍历提取所有值,避免 panic 和空指针错误。
在 Go 中处理外部 API 返回的 JSON 数据时,常需从深层嵌套结构中提取特定字段。以 Yahoo Finance API 为例,其响应中 quote 是一个 JSON 数组,对应 Go 中的切片([]struct{})。直接访问 s.Query.Results.Quote[0].LastTradePriceOnly 虽可获取首个值,但若未校验切片长度或字段有效性,程序极易 panic。
✅ 正确做法:结构化建模 + 安全遍历
首先,优化原始结构体定义,提升可读性与可维护性:
type Response struct {
Query struct {
Count int `json:"count"`
Created string `json:"created"`
Lang string `json:"lang"`
Results struct {
Quotes []Quote `json:"quote"` // 重命名 + 提升为独立类型
} `json:"results"`
} `json:"query"`
}
type Quote struct {
LastTradePriceOnly string `json:"LastTradePriceOnly"`
}接着,在 main() 中进行完整错误处理与安全遍历:
func main() {
resp, err := http.Get("http://query.yahooapis.com/v1/public/yql?q=select%20LastTradePriceOnly%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22AAPL%22,%22FB%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")
if err != nil {
fmt.Fprintf(os.Stderr, "HTTP request failed: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read response body: %v\n", err)
os.Exit(1)
}
var s Response
if err := json.Unmarshal(body, &s); err != nil {
fmt.Fprintf(os.Stderr, "JSON unmarshal failed: %v\n", err)
os.Exit(1)
}
// ✅ 安全遍历:先检查切片长度,再逐项访问
quotes := s.Query.Results.Quotes
if len(quotes) == 0 {
fmt.Println("No stock quotes returned.")
return
}
fmt.Print("Stock prices: ")
for i, q := range quotes {
if q.LastTradePriceOnly != "" { // 防空字符串(部分 API 字段可能缺失)
if i > 0 {
fmt.Print(" ")
}
fmt.Print(q.LastTradePriceOnly)
}
}
fmt.Println()
}⚠️ 关键注意事项
- 永远校验切片长度:quotes[0] 在 len(quotes)==0 时会 panic;务必用 len(quotes) > 0 判断。
- 字段可能为空或缺失:JSON 中 LastTradePriceOnly 可能为 null 或不存在,对应 Go 中为空字符串(因类型为 string);若需强类型支持,建议改用 *string 或自定义 UnmarshalJSON 方法。
- 弃用已废弃的 API:Yahoo Finance YQL 服务已于 2019 年停用,生产环境请迁移到 Alpha Vantage、Polygon.io 等现代金融 API,并启用 HTTPS 与 API key 认证。
- 使用 io.ReadAll 替代 ioutil.ReadAll:ioutil 已在 Go 1.16+ 中弃用,应导入 "io" 并使用 io.ReadAll。
✅ 总结
提取 JSON 数组字段的核心是:精准建模 → 完整错误处理 → 安全索引/遍历。避免硬编码下标(如 [0]),始终将切片视为动态集合;结合 range 循环与空值检查,即可健壮地提取所有目标值(如 52.05 114.25)。这是 Go 中处理第三方 JSON API 的标准实践。