Skip to content

Commit 0e3e2f9

Browse files
Omitzero does not work with slice and map bug (#1436)
## Fixes Fixed a bug where using omitzero still validated empty structs and maps. This PR ensures that if omitzero is used as a tag then empty slices and maps are not validated. * Modified the `baked_in.go` file to ensure that slices and maps are considered non zero only when they are not nil and they are not empty. * Added tests to ensure empty slices and maps are not validated while using omitzero tag. Check #1424 for more details. **Make sure that you've checked the boxes below before you submit PR:** - [x] Tests exist or have been written that cover this particular change. @go-playground/validator-maintainers
1 parent dfc7ccd commit 0e3e2f9

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

baked_in.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1794,7 +1794,10 @@ func hasValue(fl FieldLevel) bool {
17941794
func hasNotZeroValue(fl FieldLevel) bool {
17951795
field := fl.Field()
17961796
switch field.Kind() {
1797-
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1797+
case reflect.Slice, reflect.Map:
1798+
// For slices and maps, consider them "not zero" only if they're both non-nil AND have elements
1799+
return !field.IsNil() && field.Len() > 0
1800+
case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
17981801
return !field.IsNil()
17991802
default:
18001803
if fl.(*validate).fldIsPointer && getValue(field) != nil {

validator_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14633,3 +14633,123 @@ func TestMapEmptyPointerStructValueNoError(t *testing.T) {
1463314633
errs = validate.Struct(obj2)
1463414634
Equal(t, errs, nil)
1463514635
}
14636+
func TestOmitZeroWithSlices(t *testing.T) {
14637+
// Tests the behavior of omitempty and omitzero with slices
14638+
type OmitEmptyExample struct {
14639+
Data []string `validate:"omitempty,min=2"`
14640+
}
14641+
14642+
type OmitZeroExample struct {
14643+
Data []string `validate:"omitzero,min=2"`
14644+
}
14645+
14646+
validate := New()
14647+
14648+
t.Run("nil slices", func(t *testing.T) {
14649+
// Test 1: nil slices
14650+
test1Empty := OmitEmptyExample{Data: nil}
14651+
test1Zero := OmitZeroExample{Data: nil}
14652+
14653+
err1 := validate.Struct(test1Empty)
14654+
err2 := validate.Struct(test1Zero)
14655+
14656+
Equal(t, err1, nil) // No error (skipped)
14657+
Equal(t, err2, nil) // No error (skipped)
14658+
})
14659+
14660+
t.Run("empty but non-nil slices", func(t *testing.T) {
14661+
// Test 2: empty but non-nil slices
14662+
test2Empty := OmitEmptyExample{Data: []string{}}
14663+
test2Zero := OmitZeroExample{Data: []string{}}
14664+
14665+
err1 := validate.Struct(test2Empty)
14666+
err2 := validate.Struct(test2Zero)
14667+
14668+
NotEqual(t, err1, nil) // Error (min=2)
14669+
Equal(t, err2, nil) // No error (should be skipped)
14670+
})
14671+
14672+
t.Run("single item slices", func(t *testing.T) {
14673+
// Test 3: single item slices (still not meeting min=2)
14674+
test3Empty := OmitEmptyExample{Data: []string{"one"}}
14675+
test3Zero := OmitZeroExample{Data: []string{"one"}}
14676+
14677+
err1 := validate.Struct(test3Empty)
14678+
err2 := validate.Struct(test3Zero)
14679+
14680+
NotEqual(t, err1, nil) // Error (min=2)
14681+
NotEqual(t, err2, nil) // Error (min=2)
14682+
})
14683+
14684+
t.Run("valid slices", func(t *testing.T) {
14685+
// Test 4: valid slices (min=2 satisfied)
14686+
test4Empty := OmitEmptyExample{Data: []string{"one", "two"}}
14687+
test4Zero := OmitZeroExample{Data: []string{"one", "two"}}
14688+
14689+
err1 := validate.Struct(test4Empty)
14690+
err2 := validate.Struct(test4Zero)
14691+
14692+
Equal(t, err1, nil) // No error
14693+
Equal(t, err2, nil) // No error
14694+
})
14695+
}
14696+
func TestOmitZeroWithMaps(t *testing.T) {
14697+
// Tests the behavior of omitempty and omitzero with maps
14698+
type OmitEmptyExample struct {
14699+
Data map[string]string `validate:"omitempty,min=2"`
14700+
}
14701+
14702+
type OmitZeroExample struct {
14703+
Data map[string]string `validate:"omitzero,min=2"`
14704+
}
14705+
14706+
validate := New()
14707+
14708+
t.Run("nil maps", func(t *testing.T) {
14709+
// Test 1: nil maps
14710+
test1Empty := OmitEmptyExample{Data: nil}
14711+
test1Zero := OmitZeroExample{Data: nil}
14712+
14713+
err1 := validate.Struct(test1Empty)
14714+
err2 := validate.Struct(test1Zero)
14715+
14716+
Equal(t, err1, nil) // No error (skipped)
14717+
Equal(t, err2, nil) // No error (skipped)
14718+
})
14719+
14720+
t.Run("empty but non-nil maps", func(t *testing.T) {
14721+
// Test 2: empty but non-nil maps
14722+
test2Empty := OmitEmptyExample{Data: map[string]string{}}
14723+
test2Zero := OmitZeroExample{Data: map[string]string{}}
14724+
14725+
err1 := validate.Struct(test2Empty)
14726+
err2 := validate.Struct(test2Zero)
14727+
14728+
NotEqual(t, err1, nil) // Error (min=2)
14729+
Equal(t, err2, nil) // No error (should be skipped)
14730+
})
14731+
14732+
t.Run("single item maps", func(t *testing.T) {
14733+
// Test 3: single item maps (still not meeting min=2)
14734+
test3Empty := OmitEmptyExample{Data: map[string]string{"key1": "value1"}}
14735+
test3Zero := OmitZeroExample{Data: map[string]string{"key1": "value1"}}
14736+
14737+
err1 := validate.Struct(test3Empty)
14738+
err2 := validate.Struct(test3Zero)
14739+
14740+
NotEqual(t, err1, nil) // Error (min=2)
14741+
NotEqual(t, err2, nil) // Error (min=2)
14742+
})
14743+
14744+
t.Run("valid maps", func(t *testing.T) {
14745+
// Test 4: valid maps (min=2 satisfied)
14746+
test4Empty := OmitEmptyExample{Data: map[string]string{"key1": "value1", "key2": "value2"}}
14747+
test4Zero := OmitZeroExample{Data: map[string]string{"key1": "value1", "key2": "value2"}}
14748+
14749+
err1 := validate.Struct(test4Empty)
14750+
err2 := validate.Struct(test4Zero)
14751+
14752+
Equal(t, err1, nil) // No error
14753+
Equal(t, err2, nil) // No error
14754+
})
14755+
}

0 commit comments

Comments
 (0)