Reputation: 29
I'd like to validate the following structure :
type CarModel struct { gorm.Model OwnerID int `json:"ownerid" validate:"nonzero"` Type string `json:"type" validate:"regexp=(?)(A|B)"` A string `json:"url" validate:"isurl"` B string `json:"ip" validate:"isip"` }
I would like to validate A and B depending on Type, if type = A then A must exist and must be a URL BUT B must not exist if type = B then A must not exist and B must be an IP
is this possible with validator ?
I did try custom validation but I cannot find a way to see type value :
func checkB(v interface{}, param string) error {
theB := reflect.ValueOf(v)
if theB.Kind() != reflect.String {
return validator.ErrUnsupported
}
//check if B is an IP
ipcool := net.ParseIP(theB.String())
if ipcool == nil {
return errors.New("B : ip incorrecte " + theB.String())
}
return nil
}
Upon the answer of Alex Nichol, I would like first to thank you for your help.
If I understood correctly, I would have to iterate through all the "validate" fields, to keep a trace of the value of TYPE, A and B and then to check them depending on TYPE ...
I did this :
func checkMonitor(v interface{}) error {
var mytype string
var myA string
var myB string
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
_, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
switch field.Name {
case "Type":
mytype = fieldValue.Interface()
case "A":
myA = fieldValue.Interface()
case "B":
myB = fieldValue.Interface()
}
// Validation logic here.
//fmt.Println("field", field.Name, "has validate tag", validate, "and value", fieldValue.Interface())
}
if mytype == "A" {
if myA == "" {
return errors.New("A vide et type A")
}
ipcool := net.ParseIP(myA)
if ipcool == nil {
return errors.New("A incorrecte " + myA)
}
} else if mytype == "HTML" {
if myB == "" {
return errors.New("B vide et type B")
}
_, urlpascool := url.ParseRequestURI(myB)
if urlpascool != nil {
return errors.New("B incorrecte " + myB)
}
}
return nil
}
but got an error on the mytype, myA and myB in the switch case :
cannot use fieldValue.Interface() (type interface {}) as type string in assignment: need type assertion
EDIT : just needed to use my brain :
switch field.Name {
case "Type":
mytype = fieldValue.String()
case "A":
myA = fieldValue.String()
case "B":
myB = fieldValue.Interface()
}
Upvotes: 0
Views: 1970
Reputation: 175
Seems like your validation rules are very complicated, but you can give validating a try.
Suppose we already have two customized validators IsURL
and IsIP
, then your validation rules can be implemented as follows:
import (
"regexp"
v "github.com/RussellLuo/validating"
)
// Customized validators
var (
MatchRegexp = func(pattern string) v.Validator {
return v.FromFunc(func(field v.Field) v.Errors {
switch t := field.ValuePtr.(type) {
case *string:
if matched, _ := regexp.MatchString(pattern, *t); !matched {
return v.NewErrors(field.Name, v.ErrInvalid, "does not match")
}
return nil
default:
return v.NewErrors(field.Name, v.ErrUnsupported, "is unsupported")
}
})
}
// IsURL and IsIP are omitted
)
type CarModel struct {
gorm.Model
OwnerID int `json:"ownerid"`
Type string `json:"type"`
A string `json:"url"`
B string `json:"ip"`
}
func main() {
car := CarModel{}
errs := v.Validate(v.Schema{
v.F("ownerid", &car.OwnerID): v.Nonzero(),
v.F("type", &car.Type): MatchRegexp("(A|B)"),
v.F("a", &car.A): v.Lazy(func() v.Validator {
if car.Type == "A" {
return v.All(v.Nonzero(), IsURL())
}
return v.Not(v.Nonzero())
}),
v.F("b", &car.B): v.Lazy(func() v.Validator {
if car.Type == "B" {
return v.All(v.Nonzero(), IsIP())
}
return v.Not(v.Nonzero())
}),
})
}
Upvotes: 0
Reputation: 7510
You probably want to use reflection to iterate over the fields of the struct, get the validate
tag for each field, and check the field. This means you'll have to do validation on a struct level. Otherwise, if you pass something like myInstance.OwnerID
to a function, you'll lose the tag associated with it.
This code loops through the fields of a struct and gets the validate
tag for each:
func checkStruct(v interface{}) error {
val := reflect.ValueOf(v)
// Iterate through fields
for i := 0; i < val.NumField(); i++ {
// Lookup the validate tag
field := val.Type().Field(i)
tags := field.Tag
validate, ok := tags.Lookup("validate")
if !ok {
// No validate tag.
continue
}
// Get the value of the field.
fieldValue := val.Field(i)
// Validation logic here.
fmt.Println("field", field.Name, "has validate tag", validate, "and value",
fieldValue.Interface())
}
return nil
}
For example, we could pass it the following CarModel
:
checkStruct(CarModel{
OwnerID: 2,
Type: "B",
A: "http://google.com",
B: "192.168.1.1",
})
and it would print out the following:
field OwnerID has validate tag nonzero and value 2
field Type has validate tag regexp=(?)(A|B) and value B
field A has validate tag isurl and value http://google.com
field B has validate tag isip and value 192.168.1.1
Upvotes: 1