Validate two fields of struct with OR condition in Golang

Issue

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!

Solution

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

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:"man"`
}

// 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("validation failed")
}

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

Answered By – blackgreen

Answer Checked By – Timothy Miller (GoLangFix Admin)

Leave a Reply

Your email address will not be published.