Validator: Register custom validation on struct type

Created on 30 Apr 2019  路  10Comments  路  Source: go-playground/validator

Package version eg. v8, v9:

v9

Issue, Question or Enhancement:

Is it possible to do custom validation on a field which is of type struct? Right now the custom validator is not being called.

Code sample, to showcase or reproduce:

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
}
question

Most helpful comment

@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
}

All 10 comments

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 :)

Was this page helpful?
0 / 5 - 0 ratings