diff --git a/structs.go b/structs.go index 637245f..00a778e 100644 --- a/structs.go +++ b/structs.go @@ -35,18 +35,29 @@ func New(s interface{}) *Struct { // // Field appears in map as key "myName". // Name string `structs:"myName"` // -// A value with the content of "-" ignores that particular field. Example: +// A tag value with the content of "-" ignores that particular field. Example: // // // Field is ignored by this package. // Field bool `structs:"-"` // -// A value with the option of "omitnested" stops iterating further if the type +// A tag value with the option of "omitnested" stops iterating further if the type // is a struct. Example: // // // Field is not processed further by this package. // Field time.Time `structs:"myName,omitnested"` // Field *http.Request `structs:",omitnested"` // +// A tag value with the option of "omitempty" ignores that particular field if +// the field value is empty. Example: +// +// // Field appears in map as key "myName", but the field is +// // skipped if empty. +// Field string `structs:"myName,omitempty"` +// +// // Field appears in map as key "Field" (the default), but +// // the field is skipped if empty. +// Field string `structs:",omitempty"` +// // Note that only exported fields of a struct can be accessed, non exported // fields will be neglected. func (s *Struct) Map() map[string]interface{} { @@ -65,6 +76,17 @@ func (s *Struct) Map() map[string]interface{} { name = tagName } + // if the value is a zero value and the field is marked as omitempty do + // not include + if tagOpts.Has("omitempty") { + zero := reflect.Zero(val.Type()).Interface() + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + continue + } + } + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { // look out for embedded structs, and convert them to a // map[string]interface{} too diff --git a/structs_test.go b/structs_test.go index 5d7f04d..8ece554 100644 --- a/structs_test.go +++ b/structs_test.go @@ -148,6 +148,27 @@ func TestMap_CustomTag(t *testing.T) { } +func TestMap_OmitEmpty(t *testing.T) { + type A struct { + Name string + Value string `structs:",omitempty"` + Time time.Time `structs:",omitempty"` + } + a := A{} + + m := Map(a) + + _, ok := m["Value"].(map[string]interface{}) + if ok { + t.Error("Map should not contain the Value field that is tagged as omitempty") + } + + _, ok = m["Time"].(map[string]interface{}) + if ok { + t.Error("Map should not contain the Time field that is tagged as omitempty") + } +} + func TestMap_OmitNested(t *testing.T) { type A struct { Name string