v9
Is it possible to do custom validation on a field which is of type struct? Right now the custom validator is not being called.
type NullBool struct {
Value bool
Valid bool
}
type User struct {
IsSubaccount NullBool `validate:"isValidNullBool"`
}
var validate *validator.Validate
func init() {
validate = validator.New()
validate.RegisterValidation("isValidNullBool", isValidNullBool)
}
func isValidNullBool(fl validator.FieldLevel) bool {
nb, ok := fl.Field().Interface().(NullBool)
if !ok {
return false
}
return nb.Valid
}
Hey @iliyanmotovski currently the field level validation cannot be used with a struct, however, there is a struct level validation you can register in its stead, see here for the example.
I do hope to change this sometime in the future to have a unified validator for both, but that's a way off.
let me know if that helps :)
Hey @deankarn, to my understanding, struct level validation is called per type, this means that the validation will run _every_ time it encounters the concrete type it's registered for. However this is not the behavior I'm looking for, I want to be able to tell when to run this validation.
Another scenario: I register 2 different custom struct level validations for the same type. I need to be able to tell which validation to be executed, as I don't want the 2 of them to be called every time the said type is encountered.
If you have some workaround on mind, would be awesome :)
@iliyanmotovski @deankarn I have the same situation... If I want to use one struct in two different structs and want to validate it with different rules I have no way to do this (except duplicating it with a different name and add validation rules to it).
Example of what I need
type ProductAttributes struct {
Price int
Available bool
}
type Product {
Attributes ProductAttributes `validate:"my_custom_validation"`
// ... other fields
}
type ProductDraft {
Attributes ProductAttributes // no validation here
// ... other fields
}
@iliyanmotovski , @eugeneradionov
Here is limited possible solution, it doesn't work for deep nested structs(more than 1):
import (
"reflect"
"gopkg.in/go-playground/validator.v9"
)
type NullBool struct {
Value bool
Valid bool
}
type User struct {
IsSubaccount NullBool
}
var validate *validator.Validate
var typeOfUser = reflect.TypeOf(User{})
func init() {
validate = validator.New()
validate.RegisterStructValidation(isValidNullBoolStructUser, NullBool{})
}
func isValidNullBoolStructUser(sl validator.StructLevel) {
// check that top is expected one
okUser := indirectType(sl.Top().Type()) == typeOfUser // uniqueness based on type of Top
if !okUser {
return
}
nb := sl.Current().Interface().(NullBool)
// produce an error
if !nb.Valid {
sl.ReportError(nb.Valid, "Valid", "valid", "valid", "")
}
}
func indirectType(v reflect.Type) reflect.Type {
for v.Kind() != reflect.Ptr {
return v
}
return v.Elem()
}
Define function than checks type of Top struct of the nested.
@deankarn Any updates on this? I'm looking for a way to parameterize a StructLevel validation function via tags?
type Number struct {
Value int
}
type B struct {
NumberOne Number `validate:"only_allow=1"`
NumberTwo Number `validate:"only_allow=2"`
}
// I want to register a Number validator called only_allow
// that has access to the parameters (1,2, etc)
@devstein Did you tried oneof ?
@sergolius I checked the code and it looks like oneof doesn't support Struct
@devstein write custom validation, for now it's the one way. Try to play around with .RegisterTagNameFunc I think it's possible to register custom tag and then use it in custom validation rule. You can also register custom validation and parse it in the validation func.
@eugeneradionov Thanks! I used a custom type func to convert my struct into a primitive that validator supports with the built-in methods. My solution was roughly the following:
type Number struct {
Value int
}
type B struct {
NumberOne Number `validate:"oneof=1"`
NumberTwo Number `validate:"oneof=2"`
}
validate.RegisterCustomTypeFunc(NumberValue, Number{})
func NumberValue(v reflect.Value) interface{} {
n, ok := v.Interface().(Number)
if !ok {
return nil
}
return n.Value
}
Sorry for the radio silence, haven鈥檛 had a lot of free time lately with everything going on, thank you for everyone for jumping in and helping :)
Most helpful comment
@eugeneradionov Thanks! I used a custom type func to convert my
structinto a primitive thatvalidatorsupports with the built-in methods. My solution was roughly the following: