You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:53:17