Go语言中JSON序列化时结构体字段必须导出才能生效

在go中使用json.marshal序列化结构体切片时,若json输出为空对象(如[{},{}]),根本原因是结构体字段未导出(即首字母小写),导致json包无法访问这些字段。解决方法是将字段名首字母大写并合理使用json标签。

Go 的 encoding/json 包遵循严格的可见性规则:只有导出(exported)字段(即首字母大写的字段)才能被 JSON 编码/解码器访问。你原始定义中的 imsi、network 等字段均为小写开头,属于未导出字段,因此 json.Marshal 会忽略它们,仅生成空的 JSON 对象 {}。

✅ 正确做法如下:

  1. 将结构体字段首字母大写,使其导出;
  2. 通过 json struct tag 显式指定 JSON 键名,保持与预期的 JSON 字段命名一致(如使用下划线风格);
  3. (可选)为字段添加 omitempty 标签避免零值冗余输出。

修正后的类型定义示例:

type SpanInfo struct {
    IMSI          string `json:"imsi"`
    Network       string `json:"network"`
    NetworkStatus string `json:"network_status"`
    SignalQuality int    `json:"signal_quality"`
    Slot          int    `json:"slot"`
    State         string `json:"state"`
}

type GatewayInfo []SpanInfo

使用方式保持不变:

func getGatewayInfo(spans []SpanInfo) GatewayInfo {
    return GatewayInfo(spans)
}

// 示例初始化
spans := []SpanInfo{
    {IMSI: "652025105829193", Network: "20801", NetworkStatus: "Registered (Roaming)", SignalQuality: 17, Slot: 2, State: "active"},
    {IMSI: "652025105829194", Network: "20801",

NetworkStatus: "Registered (Roaming)", SignalQuality: 16, Slot: 3, State: "active"}, } gatewayInfo := getGatewayInfo(spans) jsonInfo, err := json.Marshal(gatewayInfo) if err != nil { log.Fatal("JSON marshal error:", err) } log.Printf("jsonInfo: %s", jsonInfo) // 输出示例: // [{"imsi":"652025105829193","network":"20801","network_status":"Registered (Roaming)","signal_quality":17,"slot":2,"state":"active"},...]

⚠️ 注意事项:

  • 若字段为指针或嵌套结构体,同样需确保其内部字段导出;
  • json:"-" 可完全忽略某字段;json:"field,omitempty" 在字段为零值时省略该键;
  • 切片类型 GatewayInfo []SpanInfo 本身无需额外实现接口,json.Marshal 原生支持切片序列化;
  • 日志中用 %v 打印结构体可清晰查看字段值,而 %s 仅适用于字符串——避免误用格式动词影响调试判断。

总结:Go 的 JSON 序列化不是“自动反射所有字段”,而是基于导出性 + struct tag 的显式契约。养成从设计阶段就规范字段命名与标签的习惯,是写出健壮序列化逻辑的关键前提。