← Backend / Go / 基础专题

28_JSON与序列化

用 encoding/json 编解码结构体,区分无效 JSON、缺失字段与错误类型。

Go 基础专题 28:JSON 编解码与字段校验

学习目标:用 encoding/json 编解码结构体,区分无效 JSON、缺失字段与错误类型。

核心知识

JSON 是跨语言交换数据的文本格式。Go 的 encoding/json 可把结构体编码成 JSON,也可把 JSON 解码到结构体。只有导出字段,即首字母大写的字段,才会被标准库正常访问;json:"title" 标签指定 JSON 中的键名。字段未出现时通常保留 Go 零值,所以业务上需要区分“未提供”和“提供了零值”。

package main

import (
    "encoding/json"
    "fmt"
)

type Note struct {
    ID    int64  `json:"id"`
    Title string `json:"title"`
    Done  bool   `json:"done"`
}

func main() {
    source := Note{ID: 1, Title: "学习 Go"}
    data, err := json.Marshal(source)
    if err != nil {
        fmt.Println("编码失败:", err)
        return
    }
    var decoded Note
    if err := json.Unmarshal(data, &decoded); err != nil {
        fmt.Println("解码失败:", err)
        return
    }
    fmt.Println(string(data), decoded.Title)
}

json.Marshal 返回 []byte 和 error,json.Unmarshal 需要目标变量的指针。把 &decoded 误写成 decoded 会失败。真实服务应向上返回错误或在入口转换成稳定提示。若输入中 title 缺失,解码通常不会报错,而得到空字符串;必须另行校验。

校验输入和控制未知字段

简单 json.Unmarshal 默认忽略结构体中没有的 JSON 字段。对需要严格输入契约的 API,可用 json.Decoder 的 DisallowUnknownFields();解码后还要检查是否有第二个 JSON 值,避免接受一个合法对象后面跟额外数据。校验应覆盖空标题、长度、数字范围和必填字段。

package main

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

type CreateNote struct {
    Title string `json:"title"`
}

func parse(raw string) (CreateNote, error) {
    decoder := json.NewDecoder(strings.NewReader(raw))
    decoder.DisallowUnknownFields()
    var request CreateNote
    if err := decoder.Decode(&request); err != nil {
        return CreateNote{}, fmt.Errorf("解析 JSON: %w", err)
    }
    if strings.TrimSpace(request.Title) == "" {
        return CreateNote{}, fmt.Errorf("title 不能为空")
    }
    return request, nil
}

func main() {
    request, err := parse(`{"title":"学习 Go"}`)
    fmt.Println(request.Title, err)
}

这个示例完成了未知字段限制和标题检查,但尚未检查第二个 JSON 值。正式 HTTP 服务可在第一次 Decode 后再解码到临时变量,要求结果是 io.EOF。HTTP 请求体还需在解码前限制最大字节数,防止大输入耗尽资源。

逐步理解

null、缺失字段和具体零值在结构体解码后可能难区分。若必须判断字段是否出现,可使用指针字段、json.RawMessage 或专门的输入 DTO,并在校验时明确允许的状态。不要直接把数据库实体作为外部请求结构体,避免暴露或接受本不该由客户端控制的字段。输出 JSON 也要检查错误,特别是待编码值含不支持的类型时。

动手练习

练习:分别调用 parse 处理合法标题、空标题、未知字段和格式错误的 JSON。答案要点:仅合法输入返回无错误;其余输入根据解析阶段或业务校验阶段返回错误。再尝试输入两个相连的 JSON 对象,补上第二次解码检查。