From 3d5a86fd1f64268f8dfc5fd49b193c5e20a23384 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Fri, 16 Apr 2021 03:16:25 +0430 Subject: [PATCH 1/8] Implement ValidateMapCtx func for validating map data --- validator_instance.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/validator_instance.go b/validator_instance.go index d230a3248..dced91e98 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -143,6 +143,19 @@ func (v *Validate) SetTagName(name string) { v.tagName = name } +// ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual +// validation validation information via context.Context. +func (v *Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]string) map[string]error { + errs := make(map[string]error) + for field, rule := range rules { + err := v.VarCtx(ctx, data[field], rule) + if err != nil { + errs[field] = err + } + } + return errs +} + // RegisterTagNameFunc registers a function to get alternate names for StructFields. // // eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: From 979e4dfd6ed87aa767d177b9ebb6cfbb157726e0 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Fri, 16 Apr 2021 03:21:15 +0430 Subject: [PATCH 2/8] Add ValidateMap func for validating map data with an empty context --- validator_instance.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/validator_instance.go b/validator_instance.go index dced91e98..6be540a9c 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -156,6 +156,11 @@ func (v *Validate) ValidateMapCtx(ctx context.Context, data map[string]interface return errs } +// ValidateMap validates map data form a map of tags +func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]string) map[string]error { + return v.ValidateMapCtx(context.Background(),data,rules) +} + // RegisterTagNameFunc registers a function to get alternate names for StructFields. // // eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names: From 19e6b81c2e3cc8b77f49bc6eb9b3eaa2265ac251 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Fri, 16 Apr 2021 03:47:14 +0430 Subject: [PATCH 3/8] Implement a basic example of ValidateMap method --- _examples/map-validation/main.go | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 _examples/map-validation/main.go diff --git a/_examples/map-validation/main.go b/_examples/map-validation/main.go new file mode 100644 index 000000000..f8093687f --- /dev/null +++ b/_examples/map-validation/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "github.com/go-playground/validator/v10" +) + +var validate *validator.Validate + +func main() { + validate = validator.New() + + validateMap() +} + +func validateMap() { + user := map[string]interface{}{"name": "Arshiya Kiani", "email": "zytel3301@gmail.com"} + + // Every rule will be applied to the item of the data that the offset of rule is pointing to. + // So if you have a field "email": "omitempty,required,email", the validator will apply these + // rules to offset of email in user data + rules := map[string]string{"name": "required,min=8,max=32", "email": "omitempty,required,email"} + + // ValidateMap will return map[string]error. + // The offset of every item in errs is the name of invalid field and the value + // is the message of error. If there was no error, ValidateMap method will + // return an EMPTY map of errors, not nil. If you want to check that + // if there was an error or not, you must check the length of the return value + errs := validate.ValidateMap(user, rules) + + if len(errs) > 0 { + fmt.Println(errs) + // The user is invalid + } + + // The user is valid +} From 3b8081571ba694e569dde16cfc8da6e9419c9585 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Mon, 19 Apr 2021 02:16:31 +0430 Subject: [PATCH 4/8] Update ValidateMapCtx method, so it can validate nested maps --- validator_instance.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/validator_instance.go b/validator_instance.go index 6be540a9c..71f698f87 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -145,12 +145,19 @@ func (v *Validate) SetTagName(name string) { // ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual // validation validation information via context.Context. -func (v *Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]string) map[string]error { - errs := make(map[string]error) +func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} { + errs := make(map[string]interface{}) for field, rule := range rules { - err := v.VarCtx(ctx, data[field], rule) - if err != nil { - errs[field] = err + if reflect.ValueOf(rule).Kind() == reflect.Map && reflect.ValueOf(data[field]).Kind() == reflect.Map { + err := v.ValidateMapCtx(ctx, data[field].(map[string]interface{}), rule.(map[string]interface{})) + if len(err) > 0 { + errs[field] = err + } + } else { + err := v.VarCtx(ctx, data[field], rule.(string)) + if err != nil { + errs[field] = err + } } } return errs From 196a379bde753c5537fcea58866d65e15593b4f1 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Mon, 19 Apr 2021 02:17:18 +0430 Subject: [PATCH 5/8] Due to changes in ValidateMapCtx, make some updates on ValidateMap method --- validator_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_instance.go b/validator_instance.go index 71f698f87..6f244cd2a 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -164,7 +164,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{ } // ValidateMap validates map data form a map of tags -func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]string) map[string]error { +func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} { return v.ValidateMapCtx(context.Background(),data,rules) } From a9e7b022cbbee18306639734a99257439eef81c5 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Mon, 19 Apr 2021 02:19:26 +0430 Subject: [PATCH 6/8] Update tests due to changes in ValidateMap method --- _examples/map-validation/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_examples/map-validation/main.go b/_examples/map-validation/main.go index f8093687f..fe3eb4059 100644 --- a/_examples/map-validation/main.go +++ b/_examples/map-validation/main.go @@ -19,7 +19,7 @@ func validateMap() { // Every rule will be applied to the item of the data that the offset of rule is pointing to. // So if you have a field "email": "omitempty,required,email", the validator will apply these // rules to offset of email in user data - rules := map[string]string{"name": "required,min=8,max=32", "email": "omitempty,required,email"} + rules := map[string]interface{}{"name": "required,min=8,max=32", "email": "omitempty,required,email"} // ValidateMap will return map[string]error. // The offset of every item in errs is the name of invalid field and the value From b20f97352819b31011eb571068ece918b9801077 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Mon, 19 Apr 2021 02:35:35 +0430 Subject: [PATCH 7/8] Create a basic test for validating nested maps --- _examples/map-validation/main.go | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/_examples/map-validation/main.go b/_examples/map-validation/main.go index fe3eb4059..92e282f5f 100644 --- a/_examples/map-validation/main.go +++ b/_examples/map-validation/main.go @@ -11,6 +11,7 @@ func main() { validate = validator.New() validateMap() + validateNestedMap() } func validateMap() { @@ -35,3 +36,38 @@ func validateMap() { // The user is valid } + +func validateNestedMap() { + + data := map[string]interface{}{ + "name": "Arshiya Kiani", + "email": "zytel3301@gmail.com", + "details": map[string]interface{}{ + "family_members": map[string]interface{}{ + "father_name": "Micheal", + "mother_name": "Hannah", + }, + "salary": "1000", + }, + } + + // Rules must be set as the structure as the data itself. If you want to dive into the + // map, just declare its rules as a map + rules := map[string]interface{}{ + "name": "min=4,max=32", + "email": "required,email", + "details": map[string]interface{}{ + "family_members": map[string]interface{}{ + "father_name": "required,min=4,max=32", + "mother_name": "required,min=4,max=32", + }, + "salary": "number", + }, + } + + if len(validate.ValidateMap(data, rules)) == 0 { + // Data is valid + } + + // Data is invalid +} From 870c9981f52ce9846d66960d0350c4014cc51a90 Mon Sep 17 00:00:00 2001 From: zytell3301 Date: Mon, 19 Apr 2021 02:57:30 +0430 Subject: [PATCH 8/8] Handle error when invalid data is supplied for diving --- validator_instance.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/validator_instance.go b/validator_instance.go index 6f244cd2a..8e27707ea 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -153,6 +153,8 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{ if len(err) > 0 { errs[field] = err } + } else if reflect.ValueOf(rule).Kind() == reflect.Map { + errs[field] = errors.New("The field: '" + field + "' is not a map to dive") } else { err := v.VarCtx(ctx, data[field], rule.(string)) if err != nil { @@ -165,7 +167,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{ // ValidateMap validates map data form a map of tags func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} { - return v.ValidateMapCtx(context.Background(),data,rules) + return v.ValidateMapCtx(context.Background(), data, rules) } // RegisterTagNameFunc registers a function to get alternate names for StructFields.