
本文介绍如何使用 go 的 `encoding/json` 包安全解析包含 `null` 元素和可选字段(如 `label`)的 json 数组,并提取结构化数据。
Go 语言原生的 json.Unmarshal 对处理含 null 值和不规则结构的 JSON 数组非常友好——关键在于正确选择目标类型。你提供的 JSON 是一个顶层 JSON 数组,其元素类型混合:既有对象(如 {"id": 27}),也有 null,且对象中字段非完全一致(label 是可选的)。直接解码为 []map[string]interface{} 虽可行,但丧失类型安全与可读性;而使用指针结构体切片([]*Item)是更优雅、健壮的方案。
以下是一个完整、可运行的示例:
package main
import (
"encoding/json"
"fmt"
)
type Item struct {
Id int `json:"id"`
Label string `json:"label,omitempty"` // omitempty 表示序列化时若为空则忽略;反序列化时自动适配缺失字段
}
func main() {
data := []byte(`[
{"id": 27},
{"id": 0, "label": "Label 0"},
null,
{"id": 93},
{"id": 85},
{"id": 54},
null,
{"id": 46, "label": "Label 46"}
]`)
var items []*Item
if err := json.Unmarshal(data, &items); err != nil {
fmt.Printf("JSON 解析失败: %v\n", err)
return
}
fmt.Println("解析结果(共", len(items), "项):")
for i, item := range items {
if item == nil {
fmt.Printf("[%d] → nil (原始 JSON 中的 null)\n", i)
} else {
fmt.Printf("[%d] → id=%d, label=%q\n", i, item.Id, item.Label)
}
}
}输出效果:
解析结果(共 8 项): [0] → id=27, label="" [1] → id=0, label="Label 0" [2] → nil (原始 JSON 中的 null) [3] → id=93, label="" [4] → id=85, label="" [5] → id=54, label="" [6] → nil (原始 JSON 中的 null) [7] → id=46, label="Label 46"
✅ 核心原理说明:
- 使用 []*Item(结构体指针切片)作为目标类型,json 包会自动将 JSON 数组中的每个对象解码为 *Item,而将 null 解码为 nil 指针,完美保留语义。
- 字段标签 json:"label,omitempty" 确保 label 缺失时不报错,且默认值为空字符串(string 零值)。
- 不需要手动判断类型或嵌套循环——Go 的 Unmarshal 已内置对多级、稀疏、含 null 的 JSON 的鲁棒支持。
⚠️ 注意事项:
- 若需严格区分 "label": "" 和 "label" 缺失,应将 Label 改为 *string 类型,并配合 omitempty;此时 nil 表示字段不存在,空指针解引用需谨慎。
- 切勿用 []Item(值类型切片)接收含 null 的数组,否则 null 位置会触发 json: cannot unmarshal null into Go value of type main.Item 错误。
- 生产环境中建议始终检查 err 并做日志/错误处理,而非仅打印后 return。
掌握这种基于结构体 + 指针切片 + 标签控制的模式,即可高效、安全地解析绝大多数现实场景中的“多层级”或“不规则” JSON 数据。










