Skip to content

Commit 1a66224

Browse files
Merge pull request #98 from Crystalix007/main
Add omitzero tag.
2 parents 75046f5 + 21da843 commit 1a66224

File tree

2 files changed

+127
-4
lines changed

2 files changed

+127
-4
lines changed

mapstructure.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,36 @@
115115
//
116116
// When decoding from a struct to any other value, you may use the
117117
// ",omitempty" suffix on your tag to omit that value if it equates to
118-
// the zero value. The zero value of all types is specified in the Go
119-
// specification.
118+
// the zero value, or a zero-length element. The zero value of all types is
119+
// specified in the Go specification.
120120
//
121121
// For example, the zero type of a numeric type is zero ("0"). If the struct
122122
// field value is zero and a numeric type, the field is empty, and it won't
123-
// be encoded into the destination type.
123+
// be encoded into the destination type. And likewise for the URLs field, if the
124+
// slice is nil or empty, it won't be encoded into the destination type.
124125
//
125126
// type Source struct {
126-
// Age int `mapstructure:",omitempty"`
127+
// Age int `mapstructure:",omitempty"`
128+
// URLs []string `mapstructure:",omitempty"`
129+
// }
130+
//
131+
// # Omit Zero Values
132+
//
133+
// When decoding from a struct to any other value, you may use the
134+
// ",omitzero" suffix on your tag to omit that value if it equates to the zero
135+
// value. The zero value of all types is specified in the Go specification.
136+
//
137+
// For example, the zero type of a numeric type is zero ("0"). If the struct
138+
// field value is zero and a numeric type, the field is empty, and it won't
139+
// be encoded into the destination type. And likewise for the URLs field, if the
140+
// slice is nil, it won't be encoded into the destination type.
141+
//
142+
// Note that if the field is a slice, and it is empty but not nil, it will
143+
// still be encoded into the destination type.
144+
//
145+
// type Source struct {
146+
// Age int `mapstructure:",omitzero"`
147+
// URLs []string `mapstructure:",omitzero"`
127148
// }
128149
//
129150
// # Unexported fields
@@ -1011,6 +1032,11 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
10111032
continue
10121033
}
10131034

1035+
// If "omitzero" is specified in the tag, it ignores zero values.
1036+
if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {
1037+
continue
1038+
}
1039+
10141040
// If "squash" is specified in the tag, we squash the field down.
10151041
squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)
10161042
if squash {

mapstructure_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,21 @@ type StructWithOmitEmpty struct {
255255
OmitNestedField *Nested `mapstructure:"omittable-nested,omitempty"`
256256
}
257257

258+
type StructWithOmitZero struct {
259+
VisibleStringField string `mapstructure:"visible-string"`
260+
OmitStringField string `mapstructure:"omittable-string,omitzero"`
261+
VisibleIntField int `mapstructure:"visible-int"`
262+
OmitIntField int `mapstructure:"omittable-int,omitzero"`
263+
VisibleFloatField float64 `mapstructure:"visible-float"`
264+
OmitFloatField float64 `mapstructure:"omittable-float,omitzero"`
265+
VisibleSliceField []interface{} `mapstructure:"visible-slice"`
266+
OmitSliceField []interface{} `mapstructure:"omittable-slice,omitzero"`
267+
VisibleMapField map[string]interface{} `mapstructure:"visible-map"`
268+
OmitMapField map[string]interface{} `mapstructure:"omittable-map,omitzero"`
269+
NestedField *Nested `mapstructure:"visible-nested"`
270+
OmitNestedField *Nested `mapstructure:"omittable-nested,omitzero"`
271+
}
272+
258273
type TypeConversionResult struct {
259274
IntToFloat float32
260275
IntToUint uint
@@ -2932,6 +2947,88 @@ func TestDecode_StructTaggedWithOmitempty_KeepNonEmptyValues(t *testing.T) {
29322947
}
29332948
}
29342949

2950+
func TestDecode_StructTaggedWithOmitzero_KeepNonZeroValues(t *testing.T) {
2951+
t.Parallel()
2952+
2953+
input := &StructWithOmitZero{
2954+
VisibleStringField: "",
2955+
OmitStringField: "string",
2956+
VisibleIntField: 0,
2957+
OmitIntField: 1,
2958+
VisibleFloatField: 0.0,
2959+
OmitFloatField: 1.0,
2960+
VisibleSliceField: nil,
2961+
OmitSliceField: []interface{}{},
2962+
VisibleMapField: nil,
2963+
OmitMapField: map[string]interface{}{},
2964+
NestedField: nil,
2965+
OmitNestedField: &Nested{},
2966+
}
2967+
2968+
var emptySlice []interface{}
2969+
var emptyMap map[string]interface{}
2970+
var emptyNested *Nested
2971+
expected := &map[string]interface{}{
2972+
"visible-string": "",
2973+
"omittable-string": "string",
2974+
"visible-int": 0,
2975+
"omittable-int": 1,
2976+
"visible-float": 0.0,
2977+
"omittable-float": 1.0,
2978+
"visible-slice": emptySlice,
2979+
"omittable-slice": []interface{}{},
2980+
"visible-map": emptyMap,
2981+
"omittable-map": map[string]interface{}{},
2982+
"visible-nested": emptyNested,
2983+
"omittable-nested": &Nested{},
2984+
}
2985+
2986+
actual := &map[string]interface{}{}
2987+
Decode(input, actual)
2988+
2989+
if !reflect.DeepEqual(actual, expected) {
2990+
t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
2991+
}
2992+
}
2993+
2994+
func TestDecode_StructTaggedWithOmitzero_DropZeroValues(t *testing.T) {
2995+
t.Parallel()
2996+
2997+
input := &StructWithOmitZero{
2998+
VisibleStringField: "",
2999+
OmitStringField: "",
3000+
VisibleIntField: 0,
3001+
OmitIntField: 0,
3002+
VisibleFloatField: 0.0,
3003+
OmitFloatField: 0.0,
3004+
VisibleSliceField: nil,
3005+
OmitSliceField: nil,
3006+
VisibleMapField: nil,
3007+
OmitMapField: nil,
3008+
NestedField: nil,
3009+
OmitNestedField: nil,
3010+
}
3011+
3012+
var emptySlice []interface{}
3013+
var emptyMap map[string]interface{}
3014+
var emptyNested *Nested
3015+
expected := &map[string]interface{}{
3016+
"visible-string": "",
3017+
"visible-int": 0,
3018+
"visible-float": 0.0,
3019+
"visible-slice": emptySlice,
3020+
"visible-map": emptyMap,
3021+
"visible-nested": emptyNested,
3022+
}
3023+
3024+
actual := &map[string]interface{}{}
3025+
Decode(input, actual)
3026+
3027+
if !reflect.DeepEqual(actual, expected) {
3028+
t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
3029+
}
3030+
}
3031+
29353032
func TestDecode_mapToStruct(t *testing.T) {
29363033
type Target struct {
29373034
String string

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy