Validator: how do i make custom validator required if another field value equal

Created on 29 Jul 2019  路  3Comments  路  Source: go-playground/validator

V9

How to make custom validator required if another field value equal

example
field A is required
field B is required if field A equal "ABC"

please help

Most helpful comment

package main

import (
    "fmt"
    "reflect"
    "strconv"
    "strings"

    "gopkg.in/go-playground/validator.v9"
)

func main() {
    validate := validator.New()

    // register custom validation: rfe(Required if Field is Equal to some value).
    validate.RegisterValidation(`rfe`, func(fl validator.FieldLevel) bool {
        param := strings.Split(fl.Param(), `:`)
        paramField := param[0]
        paramValue := param[1]

        if paramField == `` {
            return true
        }

        // param field reflect.Value.
        var paramFieldValue reflect.Value

        if fl.Parent().Kind() == reflect.Ptr {
            paramFieldValue = fl.Parent().Elem().FieldByName(paramField)
        } else {
            paramFieldValue = fl.Parent().FieldByName(paramField)
        }

        if isEq(paramFieldValue, paramValue) == false {
            return true
        }

        return hasValue(fl)
    })

    // usage demo.
    type Foo struct {
        A int `validate:"required"`
        B int `validate:"rfe=A:1"`
    }

    foo := Foo{
        A: 1,
        B: 0,
    }

    err := validate.Struct(&foo)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(`pass validation`)
    }
}

// The following functions are copied from validator.v9 lib.

func hasValue(fl validator.FieldLevel) bool {
    return requireCheckFieldKind(fl, "")
}

func requireCheckFieldKind(fl validator.FieldLevel, param string) bool {
    field := fl.Field()
    if len(param) > 0 {
        if fl.Parent().Kind() == reflect.Ptr {
            field = fl.Parent().Elem().FieldByName(param)
        } else {
            field = fl.Parent().FieldByName(param)
        }
    }
    switch field.Kind() {
    case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
        return !field.IsNil()
    default:
        _, _, nullable := fl.ExtractType(field)
        if nullable && field.Interface() != nil {
            return true
        }
        return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
    }
}

func isEq(field reflect.Value, value string) bool {
    switch field.Kind() {

    case reflect.String:
        return field.String() == value

    case reflect.Slice, reflect.Map, reflect.Array:
        p := asInt(value)

        return int64(field.Len()) == p

    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        p := asInt(value)

        return field.Int() == p

    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        p := asUint(value)

        return field.Uint() == p

    case reflect.Float32, reflect.Float64:
        p := asFloat(value)

        return field.Float() == p
    }

    panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

func asInt(param string) int64 {

    i, err := strconv.ParseInt(param, 0, 64)
    panicIf(err)

    return i
}

func asUint(param string) uint64 {

    i, err := strconv.ParseUint(param, 0, 64)
    panicIf(err)

    return i
}

func asFloat(param string) float64 {

    i, err := strconv.ParseFloat(param, 64)
    panicIf(err)

    return i
}

func panicIf(err error) {
    if err != nil {
        panic(err.Error())
    }
}

All 3 comments

package main

import (
    "fmt"
    "reflect"
    "strconv"
    "strings"

    "gopkg.in/go-playground/validator.v9"
)

func main() {
    validate := validator.New()

    // register custom validation: rfe(Required if Field is Equal to some value).
    validate.RegisterValidation(`rfe`, func(fl validator.FieldLevel) bool {
        param := strings.Split(fl.Param(), `:`)
        paramField := param[0]
        paramValue := param[1]

        if paramField == `` {
            return true
        }

        // param field reflect.Value.
        var paramFieldValue reflect.Value

        if fl.Parent().Kind() == reflect.Ptr {
            paramFieldValue = fl.Parent().Elem().FieldByName(paramField)
        } else {
            paramFieldValue = fl.Parent().FieldByName(paramField)
        }

        if isEq(paramFieldValue, paramValue) == false {
            return true
        }

        return hasValue(fl)
    })

    // usage demo.
    type Foo struct {
        A int `validate:"required"`
        B int `validate:"rfe=A:1"`
    }

    foo := Foo{
        A: 1,
        B: 0,
    }

    err := validate.Struct(&foo)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(`pass validation`)
    }
}

// The following functions are copied from validator.v9 lib.

func hasValue(fl validator.FieldLevel) bool {
    return requireCheckFieldKind(fl, "")
}

func requireCheckFieldKind(fl validator.FieldLevel, param string) bool {
    field := fl.Field()
    if len(param) > 0 {
        if fl.Parent().Kind() == reflect.Ptr {
            field = fl.Parent().Elem().FieldByName(param)
        } else {
            field = fl.Parent().FieldByName(param)
        }
    }
    switch field.Kind() {
    case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
        return !field.IsNil()
    default:
        _, _, nullable := fl.ExtractType(field)
        if nullable && field.Interface() != nil {
            return true
        }
        return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
    }
}

func isEq(field reflect.Value, value string) bool {
    switch field.Kind() {

    case reflect.String:
        return field.String() == value

    case reflect.Slice, reflect.Map, reflect.Array:
        p := asInt(value)

        return int64(field.Len()) == p

    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        p := asInt(value)

        return field.Int() == p

    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        p := asUint(value)

        return field.Uint() == p

    case reflect.Float32, reflect.Float64:
        p := asFloat(value)

        return field.Float() == p
    }

    panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

func asInt(param string) int64 {

    i, err := strconv.ParseInt(param, 0, 64)
    panicIf(err)

    return i
}

func asUint(param string) uint64 {

    i, err := strconv.ParseUint(param, 0, 64)
    panicIf(err)

    return i
}

func asFloat(param string) float64 {

    i, err := strconv.ParseFloat(param, 64)
    panicIf(err)

    return i
}

func panicIf(err error) {
    if err != nil {
        panic(err.Error())
    }
}

Thank you very much

Can anyone do this in validator v10, it seems some of the struct definitions and functions have been changed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

scisci picture scisci  路  5Comments

dkostenko picture dkostenko  路  5Comments

donileo picture donileo  路  3Comments

0505gonzalez picture 0505gonzalez  路  3Comments

alexyans picture alexyans  路  3Comments