
Go 的 json 包只能序列化/反序列化导出字段(即首字母大写的字段),若结构体字段为小写(如 id, description),则 JSON 解码时会静默忽略,导致数据为空或零值。
go 的 json 包只能序列化/反序列化导出字段(即首字母大写的字段),若结构体字段为小写(如 id, description),则 json 解码时会静默忽略,导致数据为空或零值。
在 Go 中解析 JSON 时,一个极易被初学者忽略却影响深远的关键规则是:结构体字段必须是导出的(exported)才能被 json 包访问。这意味着字段名必须以大写字母开头;否则,即使 JSON 字段名与结构体标签(json:"...")完全匹配,解码器也会跳过该字段——不会报错,也不会赋值,仅返回零值(如 0、空字符串、nil 等),从而引发“数据看似解析成功,实则为空”的典型问题。
以你提供的代码为例,原始结构体定义如下:
type Request struct {
id int `json:"id"` // ❌ 小写 id → 未导出 → 被忽略
description string `json:"description"` // ❌ 同样被忽略
user string `json:"user"`
}尽管 json:"id" 标签正确指定了 JSON 键名,但由于字段 id 是小写、非导出标识符,encoding/json 包无法反射访问它,因此 req.request.id 始终为 0,req.request.user 也为空字符串——这正是你观察到 requests: [] 或循环未执行的根本原因。
✅ 正确做法是将所有需参与 JSON 编解码的字段首字母大写,并保持 json 标签语义一致:
type Request struct {
ID int `json:"id"`
Description string `json:"description"`
User string `json:"user"`
Status string `json:"status"`
ImageThumbnail string `json:"image_thumbnail"`
}
type Requests struct {
Request Request `json:"request"` // 注意:此处 "Request" 是字段名,对应 JSON 中的 "request" 键
}
type Response struct {
Requests []Requests `json:"requests"`
Count string `json:"count"`
Benchmark float64 `json:"benchmark"`
}
type RootObject struct {
Response Response `json:"response"`
}? 提示:json 标签中的字符串(如 "id")是 JSON 键名,而结构体字段名(如 ID)是 Go 中的标识符,二者通过反射关联。字段名是否导出,决定了反射能否读写该字段。
完整可运行示例(含错误处理优化):
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
// 模拟 API 返回的 JSON 响应(生产中来自 http.Response.Body)
jsonData := `{
"response": {
"requests": [
{"request": {}},
{
"request": {
"id": 589748,
"image_thumbnail": "",
"description": "Blah blah",
"status": "received",
"user": "test"
}
}
],
"count": "50",
"benchmark": 0.95516896247864
}
}`
var root RootObject
if err := json.Unmarshal([]byte(jsonData), &root); err != nil {
panic(fmt.Sprintf("JSON decode failed: %v", err))
}
fmt.Printf("Parsed %+v\n", root)
// 安全遍历:跳过 request 为空的对象(如第一个元素)
for i, reqItem := range root.Response.Requests {
if reqItem.Request.ID != 0 { // 简单判空(实际建议检查指针或用omitempty+指针字段)
fmt.Printf("[%d] ID=%d, User=%s, Desc=%s\n",
i, reqItem.Request.ID, reqItem.Request.User, reqItem.Request.Description)
}
}
}? 关键注意事项总结:
- ✅ 所有参与 JSON 编解码的结构体字段必须首字母大写(导出);
- ✅ json 标签中的键名须与 JSON 实际字段名严格一致(区分大小写、下划线等);
- ✅ 嵌套结构需逐层确保导出性(如 RootObject.Response.Requests[0].Request.ID 中每级字段均需导出);
- ⚠️ 若 JSON 中某字段可能缺失或为 null,建议使用指针类型(如 *string)或添加 omitempty 标签配合零值判断;
- ? 调试技巧:先用 json.MarshalIndent 将结构体序列化为 JSON,对比结构是否符合预期;或打印 json.RawMessage 查看原始响应内容。
遵循这一导出规则,即可彻底避免“JSON 解析无报错但数据为空”的陷阱,让 Go 的类型安全与 JSON 互操作性真正协同工作。










