3

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!

0

2 Answers 2

6

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{}})
}
Sign up to request clarification or add additional context in comments.

1 Comment

I believe this will also be useful when having to combine a field like mobile-country and mobile number, however it will throw one field and not two, hence a need to customize more.
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)
        }
    }
    // ....
}

You can try custom tag name and multiple vaidators..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.