Validate two fields of struct with OR condition in Golang

huangapple go评论82阅读模式
英文:

Validate two fields of struct with OR condition in Golang

问题

我正在尝试验证一个包含两个字段的结构体,其中一个是必填的,另一个是可选的。

这是结构体的定义:

type man struct {
    // required: true
    Numbers []int `json:"numbers"`
    // required: false
    Pass bool `json:"pass"`
}

为了进行验证,我正在使用这个包:https://pkg.go.dev/gopkg.in/validator.v2

我的目标是创建以下条件:

Numbers 不为零 或者 pass 为 true。

然而,如果我将 numbers 定义为非零,它将阻止对 Pass 字段的检查。

我尝试创建一个自定义的验证函数,但由于其中一个字段是可选的,我不确定如何构建这些条件。

谢谢!

英文:

I'm trying to validate a struct which has two fields, one of them is required and the other one is not.

This is the struct:

type man struct {
	// required: true
	Numbers []int `json:"numbers"`
	// required: false
	Pass bool `json:"pass"`
}

In order to validate I'm using this package: https://pkg.go.dev/gopkg.in/validator.v2

My goal is to create the below condition:

Numbers is nonzero OR pass is true.

However, if I define numbers as nonzero it will block the check of the Pass field.

I've tried to create a customized validator function, but since one of the fields is not required, I'm not sure how to build this conditions.

Thanks!

答案1

得分: 6

使用更新版本的validator包(例如v9及更高版本),您可以使用required_without标签。

在验证器中,当其他指定字段中没有任何一个存在时,被验证的字段必须存在且非空。对于字符串,确保值不是""。对于切片、映射、指针、接口、通道和函数,确保值不是nil。

type man struct {
	Numbers []int  `json:"numbers" validate:"required_without=Pass"`
	Pass    bool   `json:"pass"`
}

使用不同的输入进行测试:

man{}
man{nil, false}
man{nil, true}
man{[]int{}, false}

结果为:

Key: 'man.Numbers' Error:Field validation for 'Numbers' failed on the 'required_without' tag
Key: 'man.Numbers' Error:Field validation for 'Numbers' failed on the 'required_without' tag
pass
pass

不幸的是,使用v2版本时,您只能实现单个字段的验证,因此您没有一种简单明了的方式来访问父结构体。您可能需要将结构体包装在另一个结构体中,将man视为一个字段。

type wrapper struct {
    man man `validate:"man"`
}

// 非常简单的验证函数
func manValidator(v interface{}, param string) error {
	m := v.(man)
    if m.Numbers != nil || m.Pass {
        return nil
    }
	return errors.New("validation failed")
}

func main() {
    validate.SetValidationFunc("man", manValidator)
    validator.Validate(wrapper{man{}})
}
英文:

With more recent versions of the validator package (e.g. v9 onward) you can use required_without tag.

> The field under validation must be present and not empty only when any of the other specified fields are not present. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.

type man struct {
	Numbers []int  `json:"numbers" validate:"required_without=Pass"`
	Pass    bool   `json:"pass"`
}

Testing with different inputs:

man{}
man{nil, false}
man{nil, true}
man{[]int{}, false}

Gives:

Key: 'man.Numbers' Error:Field validation for 'Numbers' failed on the 'required_without' tag
Key: 'man.Numbers' Error:Field validation for 'Numbers' failed on the 'required_without' tag
pass
pass

<hr>

Unfortunately with v2 you can only implement single field validation, so you don't really have a nice and straightforward way to access the parent struct. You might have to wrap the struct in another struct to consider man as a field.

type wrapper struct {
    man man `validate:&quot;man&quot;`
}

// Very simple validation func
func manValidator(v interface{}, param string) error {
	m := v.(man)
    if m.Numbers != nil || m.Pass {
        return nil
    }
	return errors.New(&quot;validation failed&quot;)
}

func main() {
    validate.SetValidationFunc(&quot;man&quot;, manValidator)
    validator.Validate(wrapper{man{}})
}

答案2

得分: 0

你可以尝试自定义标签名称和多个验证器。

type Man struct {
    // required: true
    Numbers []int `json:"numbers" cond1:"nonnil"`
    // required: false
    Pass bool `json:"pass" cond2:"nonzero"`
}

func main() {
    // .... 
    cond1Validator := validator.NewValidator()
    cond1Validator.SetTag("cond1")
    cond2Validator := validator.NewValidator()
    cond2Validator.SetTag("cond2")
    if errs := cond1Validator.Validate(man); errs != nil {
        if errs := cond2Validator.Validate(man); errs != nil {
            log.Fatalln(errs)
        }
    }
    // ....
}

你可以尝试自定义标签名称和多个验证器。

英文:
type Man struct {
    // required: true
    Numbers []int `json:&quot;numbers&quot; cond1:&quot;nonnil&quot;`
    // required: false
    Pass bool `json:&quot;pass&quot; cond2:&quot;nonzero&quot;`
}

func main() {
    // .... 
    cond1Validator := validator.NewValidator()
    cond1Validator.SetTag(&quot;cond1&quot;)
    cond2Validator := validator.NewValidator()
    cond2Validator.SetTag(&quot;cond2&quot;)
    if errs := cond1Validator.Validate(man); errs != nil {
        if errs := cond2Validator.Validate(man); errs != nil {
            log.Fatalln(errs)
        }
    }
    // ....
}

You can try custom tag name and multiple vaidators..

huangapple
  • 本文由 发表于 2022年5月17日 13:53:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/72268799.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定