Skip to content

Commit d3e4be3

Browse files
authored
Enhanced ValidationCtx method to support nested map in slice #915 (#917)
1 parent 99922fc commit d3e4be3

File tree

3 files changed

+122
-8
lines changed

3 files changed

+122
-8
lines changed

_examples/map-validation/main.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ func validateNestedMap() {
4848
"mother_name": "Hannah",
4949
},
5050
"salary": "1000",
51+
"phones": []map[string]interface{}{
52+
{
53+
"number": "11-111-1111",
54+
"remark": "home",
55+
},
56+
{
57+
"number": "22-222-2222",
58+
"remark": "work",
59+
},
60+
},
5161
},
5262
}
5363

@@ -62,6 +72,10 @@ func validateNestedMap() {
6272
"mother_name": "required,min=4,max=32",
6373
},
6474
"salary": "number",
75+
"phones": map[string]interface{}{
76+
"number": "required,min=4,max=32",
77+
"remark": "required,min=1,max=32",
78+
},
6579
},
6680
}
6781

validator_instance.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,24 @@ func (v *Validate) SetTagName(name string) {
154154
func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
155155
errs := make(map[string]interface{})
156156
for field, rule := range rules {
157-
if reflect.ValueOf(rule).Kind() == reflect.Map && reflect.ValueOf(data[field]).Kind() == reflect.Map {
158-
err := v.ValidateMapCtx(ctx, data[field].(map[string]interface{}), rule.(map[string]interface{}))
159-
if len(err) > 0 {
160-
errs[field] = err
157+
if ruleObj, ok := rule.(map[string]interface{}); ok {
158+
if dataObj, ok := data[field].(map[string]interface{}); ok {
159+
err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
160+
if len(err) > 0 {
161+
errs[field] = err
162+
}
163+
} else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
164+
for _, obj := range dataObjs {
165+
err := v.ValidateMapCtx(ctx, obj, ruleObj)
166+
if len(err) > 0 {
167+
errs[field] = err
168+
}
169+
}
170+
} else {
171+
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
161172
}
162-
} else if reflect.ValueOf(rule).Kind() == reflect.Map {
163-
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
164-
} else {
165-
err := v.VarCtx(ctx, data[field], rule.(string))
173+
} else if ruleStr, ok := rule.(string); ok {
174+
err := v.VarCtx(ctx, data[field], ruleStr)
166175
if err != nil {
167176
errs[field] = err
168177
}

validator_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12134,6 +12134,97 @@ func TestPostCodeByIso3166Alpha2Field_InvalidKind(t *testing.T) {
1213412134
t.Errorf("Didn't panic as expected")
1213512135
}
1213612136

12137+
func TestValidate_ValidateMapCtx(t *testing.T) {
12138+
12139+
type args struct {
12140+
data map[string]interface{}
12141+
rules map[string]interface{}
12142+
}
12143+
tests := []struct {
12144+
name string
12145+
args args
12146+
want int
12147+
}{
12148+
{
12149+
name: "test nested map in slice",
12150+
args: args{
12151+
data: map[string]interface{}{
12152+
"Test_A": map[string]interface{}{
12153+
"Test_B": "Test_B",
12154+
"Test_C": []map[string]interface{}{
12155+
{
12156+
"Test_D": "Test_D",
12157+
},
12158+
},
12159+
"Test_E": map[string]interface{}{
12160+
"Test_F": "Test_F",
12161+
},
12162+
},
12163+
},
12164+
rules: map[string]interface{}{
12165+
"Test_A": map[string]interface{}{
12166+
"Test_B": "min=2",
12167+
"Test_C": map[string]interface{}{
12168+
"Test_D": "min=2",
12169+
},
12170+
"Test_E": map[string]interface{}{
12171+
"Test_F": "min=2",
12172+
},
12173+
},
12174+
},
12175+
},
12176+
want: 0,
12177+
},
12178+
12179+
{
12180+
name: "test nested map error",
12181+
args: args{
12182+
data: map[string]interface{}{
12183+
"Test_A": map[string]interface{}{
12184+
"Test_B": "Test_B",
12185+
"Test_C": []interface{}{"Test_D"},
12186+
"Test_E": map[string]interface{}{
12187+
"Test_F": "Test_F",
12188+
},
12189+
"Test_G": "Test_G",
12190+
"Test_I": []map[string]interface{}{
12191+
{
12192+
"Test_J": "Test_J",
12193+
},
12194+
},
12195+
},
12196+
},
12197+
rules: map[string]interface{}{
12198+
"Test_A": map[string]interface{}{
12199+
"Test_B": "min=2",
12200+
"Test_C": map[string]interface{}{
12201+
"Test_D": "min=2",
12202+
},
12203+
"Test_E": map[string]interface{}{
12204+
"Test_F": "min=100",
12205+
},
12206+
"Test_G": map[string]interface{}{
12207+
"Test_H": "min=2",
12208+
},
12209+
"Test_I": map[string]interface{}{
12210+
"Test_J": "min=100",
12211+
},
12212+
},
12213+
},
12214+
},
12215+
want: 1,
12216+
},
12217+
}
12218+
for _, tt := range tests {
12219+
t.Run(tt.name, func(t *testing.T) {
12220+
validate := New()
12221+
if got := validate.ValidateMapCtx(context.Background(), tt.args.data, tt.args.rules); len(got) != tt.want {
12222+
t.Errorf("ValidateMapCtx() = %v, want %v", got, tt.want)
12223+
}
12224+
})
12225+
}
12226+
}
12227+
1213712228
func TestCreditCardFormatValidation(t *testing.T) {
1213812229
tests := []struct {
1213912230
value string `validate:"credit_card"`

0 commit comments

Comments
 (0)