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标签告诉验证器需要递归验证切片中的每个元素。

回到顶部