贝利信息

如何在Golang中实现JSON校验_Golang encoding/json与struct标签方法

日期:2026-01-25 00:00 / 作者:P粉602998670
Go JSON解析失败时需先检查字段是否导出、json标签拼写是否正确(如json:"name")、类型是否匹配并合理使用omitempty;再用json.RawMessage延迟解析不确定结构;关键字段可自定义UnmarshalJSON校验;推荐用validator库声明式校验。

JSON 解析失败时如何定位 struct 标签问题

Go 的 encoding/json 在解析失败时通常只报 json.UnmarshalTypeErrorjson.SyntaxError,但不会直接告诉你哪个字段的 struct 标签写错了。常见诱因是字段未导出(首字母小写)、json 标签拼写错误(如写成 josn)、或类型不匹配却没设 omitempty 导致空值无法赋给非指针非零值字段。

用 json.RawMessage 延迟校验嵌套结构

当 JSON 中某字段结构不确定(比如可能是对象、数组或字符串),直接绑定到具体 struct 会 panic。用

json.RawMessage 可先跳过解析,后续按需校验。

type Payload struct {
    ID    int              `json:"id"`
    Data  json.RawMessage  `json:"data"`
}

var p Payload
if err := json.Unmarshal(b, &p); err != nil {
    return err
}
// 后续再判断 data 是什么类型,再 Unmarshal 到对应 struct

自定义 UnmarshalJSON 实现细粒度校验

标准库不提供字段级必填/格式校验能力。要实现如“email 字段必须含 @”或“price 必须 ≥ 0”,得重写 UnmarshalJSON 方法。

type Order struct {
    ID     int     `json:"id"`
    Email  string  `json:"email"`
    Price  float64 `json:"price"`
}

func (o *Order) UnmarshalJSON(data []byte) error {
    type Alias Order // 防止递归调用
    aux := &struct {
        *Alias
        Email string `json:"email"`
        Price float64 `json:"price"`
    }{
        Alias: (*Alias)(o),
    }
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    if !strings.Contains(aux.Email, "@") {
        return fmt.Errorf("email must contain '@'")
    }
    if aux.Price < 0 {
        return fmt.Errorf("price must be non-negative")
    }
    o.Email = aux.Email
    o.Price = aux.Price
    return nil
}

用第三方库做声明式校验(如 go-playground/validator)

手写 UnmarshalJSON 易出错且重复。更推荐用 validator 库,在 struct 标签中声明规则,解码后再统一校验。

import "github.com/go-playground/validator/v10"

type User struct {
    Name  string `json:"name" validate:"required,min=2,max=20"`
    Email string `json:"email" validate:"required,email"`
    Age   uint8  `json:"age" validate:"gte=0,lte=150"`
}

var u User
if err := json.Unmarshal(b, &u); err != nil {
    return err
}
validate := validator.New()
if err := validate.Struct(u); err != nil {
    return err // 返回具体哪个字段、哪条规则失败
}
字段可导出、标签拼写、类型匹配这三关没过,再强的校验库也救不了——它们都发生在解码之后。