JSON 编码性能怎么优化?struct tag 有哪些细节?

一句话结论:encoding/json 靠反射实现,开箱即用但不算快;性能敏感时减少反射面(结构体小、字段少)、复用编码对象与缓冲,必要时换 jsoniter/easyjson 或手写 MarshalJSON。struct tag 控制字段名、忽略、空值省略与数字字符串化,其中 omitempty 的“零值即省略”最容易踩坑。

struct tag 速查

tag效果
json:"name"指定序列化字段名不写则默认用 Go 字段名
json:"-"完全忽略该字段与 “,-” 不同(后者字段名叫 -)
,omitempty零值省略false/0/""/空切片都会被省,无法区分“没填”与“填了零值”
,string数字/布尔编码为 JSON 字符串顺序写法 json:"id,string,omitempty"
无 tag 的未导出字段直接忽略反序列化进私有字段既不报错也不生效

要区分“没传”与“传了零值”时,用指针字段(nil 表示没传)或自定义 MarshalJSON/UnmarshalJSON。

性能优化要点

  • 用具体结构体代替 map[string]any:省掉装箱与字符串 key 开销,且类型安全。
  • Decoder/Encoder 设置:UseNumber 保留精度;Encoder.SetEscapeHTML(false) 关闭 <>& 转义。
  • 复用:json.Encoder 不并发安全,可放 sync.Pool 复用内部缓冲。
  • 大整数与金额:float64 超过 2^53 会丢精度,改用 json.Number、string 或自建类型。
  • 极热路径再考虑 jsoniter/easyjson 或手写编解码。

代码示例

type Order struct {
	ID     int64   `json:"id,string"`          // 前端友好:数字串
	User   string  `json:"user,omitempty"`     // 空串不输出
	Remark *string `json:"remark,omitempty"`   // nil 才省略,能区分零值
	secret string                             // 未导出:永远不参与
}

var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
_ = enc.Encode(order)

常见追问 / 记忆点

  • 追问:time.Time 序列化出来是什么格式?——RFC3339 字符串,因为 time.Time 实现了 Marshaler/Unmarshaler。
  • 追问:反序列化遇到未知字段会报错吗?——默认静默忽略;需要严格校验用 Decoder.DisallowUnknownFields。
  • 记忆点:omitempty 是“零值省略”而非“空值省略”;要区分没填/填零值时用指针或自定义编解码。
笔记加载中…