如何在 Go 中正确解析 JSON 并填充结构体字段

go 的 `encoding/json` 包仅能序列化和反序列化导出(首字母大写)的结构体字段;若字段为小写(未导出),json 解析将静默失败,导致结构体字段为空。

在 Go 中解析 JSON 时,一个常见却容易被忽视的陷阱是:结构体字段必须是导出字段(即首字母大写),否则 json.Unmarshal 或 json.NewDecoder.Decode 将无法为其赋值——即使不报错,字段也始终为空字符串、零值或 nil。

这是因为 Go 的 encoding/json 包基于反射(reflection)工作,而反射只能访问导出(public)字段。未导出字段(如 id string)对包外代码(包括标准库)不可见,因此 JSON

解析器会跳过它们,且不会返回错误。

以下是一个修复后的完整示例:

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

type RefreshTokenData struct {
    Id            string `json:"id"`
    IssuedAt      string `json:"issued_at"`     // 推荐使用驼峰命名,更符合 Go 风格
    Scope         string `json:"scope"`
    InstanceURL   string `json:"instance_url"`
    TokenType     string `json:"token_type"`
    RefreshToken  string `json:"refresh_token"`
    IDToken       string `json:"id_token"`      // 注意:原 JSON 含此字段,原结构体缺失
    Signature     string `json:"signature"`
    AccessToken   string `json:"access_token"`
}

func main() {
    tokenResp := `{
        "id": "https://google.com",
        "issued_at": "1423698767063",
        "scope": "full refresh_token",
        "instance_url": "https://na15.salesforce.com",
        "token_type": "Bearer",
        "refresh_token": "2os53__CCU5JX_yZXE",
        "id_token": "5jSH0Oqm7Q4fc0xkE9NOvW8cA13U",
        "signature": "/599EkGVIBsKPFRNkg+58wZ3Q7AFyclvIGvCrxVeyTo=",
        "access_token": "sadfasdfasdfasdfdsa"
    }`

    var tokenData RefreshTokenData
    if err := json.Unmarshal([]byte(tokenResp), &tokenData); err != nil {
        fmt.Printf("❌ JSON 解析失败: %v\n", err)
        return
    }

    fmt.Printf("✅ 成功解析:\n")
    fmt.Printf("- RefreshToken: %q\n", tokenData.RefreshToken)
    fmt.Printf("- AccessToken:  %q\n", tokenData.AccessToken)
    fmt.Printf("- IDToken:      %q\n", tokenData.IDToken)
}
✅ 输出示例:✅ 成功解析:- RefreshToken: "2os53__CCU5JX_yZXE"- AccessToken: "sadfasdfasdfasdfdsa"- IDToken: "5jSH0Oqm7Q4fc0xkE9NOvW8cA13U"

关键注意事项:

  • 字段必须导出:所有需参与 JSON 编解码的字段名首字母必须大写(如 RefreshToken 而非 refresh_token);
  • Tag 映射仍有效:json:"refresh_token" 标签仅控制 JSON 键名映射,不影响导出性;
  • 字段完整性很重要:原始 JSON 中存在 "id_token" 字段,但初始结构体未定义,会导致该字段丢失;建议对照 JSON 响应完整定义结构体,或使用 map[string]interface{} + 类型断言做动态解析;
  • 推荐命名风格:Go 社区惯例使用 CamelCase(如 IssuedAt, InstanceURL)而非下划线,既保持导出性,又提升可读性;
  • 错误处理不可省略:虽然本例中解析成功,但生产环境务必检查 err,避免静默失败掩盖逻辑问题。

遵循以上原则,即可确保 Go 中 JSON 解析稳定、可靠、可维护。