Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: support validate struct without struct tag #934

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 92 additions & 0 deletions _examples/struct-map-rules-validation/main.go
@@ -0,0 +1,92 @@
package main

import (
"fmt"
"github.com/go-playground/validator/v10"
)

type Data struct {
Name string
Email string
Details *Details
}

type Details struct {
FamilyMembers *FamilyMembers
Salary string
}

type FamilyMembers struct {
FatherName string
MotherName string
}

type Data2 struct {
Name string
Age uint32
}

var validate = validator.New()

func main() {
validateStruct()
// output
// Key: 'Data2.Name' Error:Field validation for 'Name' failed on the 'min' tag
// Key: 'Data2.Age' Error:Field validation for 'Age' failed on the 'max' tag

validateStructNested()
// output
// Key: 'Data.Name' Error:Field validation for 'Name' failed on the 'max' tag
// Key: 'Data.Details.FamilyMembers' Error:Field validation for 'FamilyMembers' failed on the 'required' tag
}

func validateStruct() {
data := Data2{
Name: "leo",
Age: 1000,
}

rules := map[string]string{
"Name": "min=4,max=6",
"Age": "min=4,max=6",
}

validate.RegisterStructValidationMapRules(rules, Data2{})

err := validate.Struct(data)
fmt.Println(err)
fmt.Println()
}

func validateStructNested() {
data := Data{
Name: "11sdfddd111",
Email: "zytel3301@mail.com",
Details: &Details{
Salary: "1000",
},
}

rules1 := map[string]string{
"Name": "min=4,max=6",
"Email": "required,email",
"Details": "required",
}

rules2 := map[string]string{
"Salary": "number",
"FamilyMembers": "required",
}

rules3 := map[string]string{
"FatherName": "required,min=4,max=32",
"MotherName": "required,min=4,max=32",
}

validate.RegisterStructValidationMapRules(rules1, Data{})
validate.RegisterStructValidationMapRules(rules2, Details{})
validate.RegisterStructValidationMapRules(rules3, FamilyMembers{})
err := validate.Struct(data)

fmt.Println(err)
}
9 changes: 7 additions & 2 deletions cache.go
Expand Up @@ -114,12 +114,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}

numFields := current.NumField()
rules := v.rules[typ]

var ctag *cTag
var fld reflect.StructField
var tag string
var customName string

for i := 0; i < numFields; i++ {

fld = typ.Field(i)
Expand All @@ -128,7 +129,11 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
continue
}

tag = fld.Tag.Get(v.tagName)
if rtag, ok := rules[fld.Name]; ok {
tag = rtag
} else {
tag = fld.Tag.Get(v.tagName)
}

if tag == skipValidationTag {
continue
Expand Down
28 changes: 28 additions & 0 deletions validator_instance.go
Expand Up @@ -84,6 +84,7 @@ type Validate struct {
aliases map[string]string
validations map[string]internalValidationFuncWrapper
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
rules map[reflect.Type]map[string]string
tagCache *tagCache
structCache *structCache
}
Expand Down Expand Up @@ -271,6 +272,33 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i
}
}

// RegisterStructValidationMapRules registers validate map rules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some addition information regarding that rules override any tags at the struct level?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I can add it.

//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
if v.rules == nil {
v.rules = make(map[reflect.Type]map[string]string)
}

deepCopyRules := make(map[string]string)
for i, rule := range rules {
deepCopyRules[i] = rule
}

for _, t := range types {
typ := reflect.TypeOf(t)

if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}

if typ.Kind() != reflect.Struct {
continue
}
v.rules[typ] = deepCopyRules
}
}

// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
Expand Down