如何使用Go-playground Validator验证JSON字符串?求更优方案
问题描述
我用github.com/go-playground/validator验证结构体,但想确保输入的JSON字符串完全符合要求。试了下面的代码:
type User struct { Username string `json:"username" validate:"required"` Age int `json:"age" validate:"required,gte=0"` } func main() { jsonStr := `{ "username": "123455", "age": "12" }` user := User{} jsonBytes := []byte(jsonStr) if !json.Valid(jsonBytes) { log.Fatal("input is not a valid json") } err := json.Unmarshal(jsonBytes, &user) if err != nil { log.Fatal(err.Error()) } log.Print(user) validate := validator.New() err = validate.Struct(user) if err != nil { log.Fatal(err.Error()) } log.Print("valid") }
但发现JSON里age字段类型和结构体定义不符时,json.Unmarshal直接报错:
json: cannot unmarshal string into Go struct field User.age of type int
根本到不了validate.Struct的校验环节。有没有更优的方法能从一开始就验证JSON字符串的有效性?
解决方案
可以通过两种实用思路解决这个问题:
1. 用中间结构体兼容类型,再转目标结构体
先定义一个中间结构体,把需要兼容类型的字段设为interface{},先将JSON反序列化到中间结构体(不会因类型不符报错),手动完成类型校验和转换后,再转成目标结构体,最后用validator做业务规则校验。
示例代码:
import ( "encoding/json" "log" "strconv" "github.com/go-playground/validator/v10" ) type User struct { Username string `json:"username" validate:"required"` Age int `json:"age" validate:"required,gte=0"` } // 中间结构体用于接收原始JSON字段 type RawUser struct { Username interface{} `json:"username"` Age interface{} `json:"age"` } func main() { jsonStr := `{ "username": "123455", "age": "12" }` var rawUser RawUser jsonBytes := []byte(jsonStr) // 先校验JSON格式合法性 if !json.Valid(jsonBytes) { log.Fatal("input is not a valid json") } // 反序列化到中间结构体,避免类型不匹配报错 err := json.Unmarshal(jsonBytes, &rawUser) if err != nil { log.Fatal(err.Error()) } // 手动校验字段类型并转换为目标结构体 user := User{} // 处理Username字段 if username, ok := rawUser.Username.(string); ok { user.Username = username } else { log.Fatal("username must be string type") } // 处理Age字段:支持int或可转int的string var age int switch v := rawUser.Age.(type) { case int: age = v case string: age, err = strconv.Atoi(v) if err != nil { log.Fatal("age must be integer or numeric string") } default: log.Fatal("age must be integer or numeric string") } user.Age = age // 执行业务规则校验 validate := validator.New() err = validate.Struct(user) if err != nil { log.Fatal(err.Error()) } log.Print("valid") log.Print(user) }
2. 自定义JSON反序列化方法
给目标结构体实现json.Unmarshaler接口,在反序列化过程中处理类型兼容和校验,把类型校验和业务校验统一到一个环节。
示例代码:
import ( "encoding/json" "errors" "log" "strconv" "github.com/go-playground/validator/v10" ) type User struct { Username string `json:"username" validate:"required"` Age int `json:"age" validate:"required,gte=0"` } func (u *User) UnmarshalJSON(data []byte) error { // 临时结构体接收原始字段 type tempUser struct { Username interface{} `json:"username"` Age interface{} `json:"age"` } var temp tempUser if err := json.Unmarshal(data, &temp); err != nil { return err } // 校验并转换Username if username, ok := temp.Username.(string); ok { u.Username = username } else { return errors.New("username must be string type") } // 校验并转换Age var age int switch v := temp.Age.(type) { case int: age = v case string: var err error age, err = strconv.Atoi(v) if err != nil { return errors.New("age must be integer or numeric string") } default: return errors.New("age must be integer or numeric string") } u.Age = age // 可选:在这里直接执行业务规则校验 validate := validator.New() return validate.Struct(u) } func main() { jsonStr := `{ "username": "123455", "age": "12" }` var user User jsonBytes := []byte(jsonStr) if !json.Valid(jsonBytes) { log.Fatal("input is not a valid json") } err := json.Unmarshal(jsonBytes, &user) if err != nil { log.Fatal(err.Error()) } log.Print("valid") log.Print(user) }
补充说明
json.Valid仅能校验JSON格式是否合法,无法校验字段类型和业务规则,必须配合后续的类型校验步骤。- 如果需要支持更复杂的类型兼容(比如float转int),可以在类型转换逻辑里扩展对应处理逻辑。
内容的提问来源于stack exchange,提问作者Jiew Meng
相关产品推荐
相关产品推荐

