diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 2c613ded2..80373104f 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -9,6 +9,7 @@ package gvalid import ( "context" + "reflect" "regexp" "strings" @@ -22,9 +23,10 @@ type CustomMsg = map[string]interface{} // fieldRule defined the alias name and rule string for specified field. type fieldRule struct { - Name string // Alias name for the field. - Rule string // Rule string like: "max:6" - IsMeta bool // Is this rule is from gmeta.Meta, which marks it as whole struct rule. + Name string // Alias name for the field. + Rule string // Rule string like: "max:6" + IsMeta bool // Is this rule is from gmeta.Meta, which marks it as whole struct rule. + FieldKind reflect.Kind // Kind of struct field, which is used for parameter type checks. } // iNoValidation is an interface that marks current struct not validated by package `gvalid`. @@ -43,6 +45,8 @@ const ( noValidationTagName = "nv" // no validation tag name for struct attribute. ruleNameBail = "bail" // the name for rule "bail" ruleNameCi = "ci" // the name for rule "ci" + emptyJsonArrayStr = "[]" // Empty json string for array type. + emptyJsonObjectStr = "{}" // Empty json string for object type. ) var ( diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 38d570b34..42244db40 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -177,9 +177,10 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error _, isMeta = fieldValue.(gmeta.Meta) } checkRules = append(checkRules, fieldRule{ - Name: name, - Rule: rule, - IsMeta: isMeta, + Name: name, + Rule: rule, + IsMeta: isMeta, + FieldKind: field.OriginalKind(), }) } } else { @@ -278,6 +279,19 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error } } } + // Empty json string checks according to mapping field kind. + if value != nil { + switch checkRuleItem.FieldKind { + case reflect.Struct, reflect.Map: + if gconv.String(value) == emptyJsonObjectStr { + value = "" + } + case reflect.Slice, reflect.Array: + if gconv.String(value) == emptyJsonArrayStr { + value = "" + } + } + } // It checks each rule and its value in loop. if validatedError := v.doCheckValue(ctx, doCheckValueInput{ Name: checkRuleItem.Name, @@ -302,6 +316,11 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error required = true break } + // All custom validation rules are required rules. + if _, ok := customRuleFuncMap[ruleKey]; ok { + required = true + break + } } if !required { continue diff --git a/util/gvalid/gvalid_z_unit_feature_recursive_test.go b/util/gvalid/gvalid_z_unit_feature_recursive_test.go index f61be8431..71009b0f2 100755 --- a/util/gvalid/gvalid_z_unit_feature_recursive_test.go +++ b/util/gvalid/gvalid_z_unit_feature_recursive_test.go @@ -216,8 +216,6 @@ func Test_CheckMap_Recursive_SliceStruct(t *testing.T) { } func Test_CheckStruct_Recursively_SliceAttribute(t *testing.T) { - // TODO - return gtest.C(t, func(t *gtest.T) { type Student struct { Name string `v:"required#Student Name is required"` @@ -235,8 +233,9 @@ func Test_CheckStruct_Recursively_SliceAttribute(t *testing.T) { } ) err := g.Validator().Assoc(data).Data(teacher).Run(ctx) - t.Assert(err, `Student Name is required`) + t.Assert(err, `The Students field is required`) }) + gtest.C(t, func(t *gtest.T) { type Student struct { Name string `v:"required#Student Name is required"`