Golang中如何验证结构体数组的JSON数据
Golang中如何验证结构体数组的JSON数据 我正在尝试验证JSON数组。首先,我将其反序列化到结构体数组中。
package main
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator"
)
var validate *validator.Validate
type Event struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
func main() {
payload := `[{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]`
validate := validator.New()
var plStruct []Event
fmt.Printf("PayLoad: %s\n\n", payload)
if err := json.Unmarshal([]byte(payload), &plStruct); err != nil {
fmt.Printf("PayLoad Struct: %s", plStruct)
fmt.Printf("Error in payload: %s\n", err)
}
fmt.Printf("PayLoad Struct: %s\n", plStruct)
if err := validate.Struct(plStruct); err != nil {
fmt.Printf("Error Validating Payload: %s\n", err)
} else {
fmt.Printf("no issues...")
}
}
错误信息:
PayLoad: [{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]
PayLoad Struct: [{bitrate_adjusted quality} {play playback}]
Error Validating Payload: validator: (nil []main.Event)
有没有办法在不使用循环的情况下直接验证事件数组?
更多关于Golang中如何验证结构体数组的JSON数据的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
你好!
validate 结构体仅验证结构体本身。在你代码的那部分,你试图验证的是切片而不是结构体,你需要遍历所有元素并逐一调用验证,或者你可以使用深度验证(deep dive)功能,这需要一个包含切片的结构体。
package main
import (
"fmt"
"reflect"
"strings"
"github.com/go-playground/validator/v10"
)
// User contains user information
type User struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `json:"e-mail" validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
请参考这里 User 结构体中 Addresses 字段的用法。
就我个人而言,我反对使用结构体标签来进行验证,因为这个包在运行时使用了反射。
更多关于Golang中如何验证结构体数组的JSON数据的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,go-playground/validator库默认不支持直接验证切片或数组。验证器需要明确知道要验证的是切片本身还是切片中的每个元素。以下是两种解决方案:
方案1:创建包装结构体(推荐)
package main
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Event struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
// 包装结构体,用于验证数组
type Events struct {
Events []Event `validate:"required,dive"`
}
func main() {
payload := `[{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]`
validate := validator.New()
// 方法1:使用包装结构体
var events Events
if err := json.Unmarshal([]byte(payload), &events.Events); err != nil {
fmt.Printf("Error unmarshaling: %s\n", err)
return
}
fmt.Printf("Events: %+v\n", events.Events)
if err := validate.Struct(events); err != nil {
fmt.Printf("Error Validating Events: %s\n", err)
} else {
fmt.Println("Validation passed!")
}
}
方案2:使用dive标签直接验证切片
package main
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Event struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
func main() {
payload := `[{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]`
validate := validator.New()
var events []Event
if err := json.Unmarshal([]byte(payload), &events); err != nil {
fmt.Printf("Error unmarshaling: %s\n", err)
return
}
fmt.Printf("Events: %+v\n", events)
// 直接验证切片,使用dive验证每个元素
if err := validate.Var(events, "required,dive"); err != nil {
fmt.Printf("Error Validating Events: %s\n", err)
} else {
fmt.Println("Validation passed!")
}
}
方案3:自定义验证函数
package main
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Event struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
func main() {
payload := `[{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]`
validate := validator.New()
var events []Event
if err := json.Unmarshal([]byte(payload), &events); err != nil {
fmt.Printf("Error unmarshaling: %s\n", err)
return
}
fmt.Printf("Events: %+v\n", events)
// 验证每个事件
for i, event := range events {
if err := validate.Struct(event); err != nil {
fmt.Printf("Error validating event at index %d: %s\n", i, err)
return
}
}
fmt.Println("All events validated successfully!")
}
方案4:使用结构体验证切片字段
package main
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
)
type Event struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required,oneof=quality playback"`
}
type Request struct {
Events []Event `json:"events" validate:"required,min=1,dive"`
}
func main() {
payload := `{"events": [{"name":"bitrate_adjusted","type":"quality"}, {"name":"play","type":"playback"}]}`
validate := validator.New()
var req Request
if err := json.Unmarshal([]byte(payload), &req); err != nil {
fmt.Printf("Error unmarshaling: %s\n", err)
return
}
fmt.Printf("Request: %+v\n", req)
if err := validate.Struct(req); err != nil {
fmt.Printf("Validation Error: %s\n", err)
} else {
fmt.Println("Validation passed!")
}
}
以上方案中,方案1和方案2是最直接的,不需要循环即可验证整个数组。dive标签告诉验证器需要递归验证切片中的每个元素。

