diff --git a/encoder.go b/encoder.go index 52f2c10..74be9a6 100644 --- a/encoder.go +++ b/encoder.go @@ -11,8 +11,9 @@ type encoderFunc func(reflect.Value) string // Encoder encodes values from a struct into url.Values. type Encoder struct { - cache *cache - regenc map[reflect.Type]encoderFunc + cache *cache + regenc map[reflect.Type]encoderFunc + omitEmpty bool } // NewEncoder returns a new Encoder with defaults. @@ -40,6 +41,16 @@ func (e *Encoder) SetAliasTag(tag string) { e.cache.tag = tag } +// SetOmitEmptyDefault allows to set omit empty behaviour by default. +// When pointers to values are used in fields it is required to use omitempty to avoid +// encoding them as 'null' (which are not decodable by Decoder). This function allows +// to make omitempty behaviour to be a default, rather than requiring to specify +// per field tag. +// The default is false, i.e. one should specify 'omitempty' in tags as usual. +func (e *Encoder) SetOmitEmptyDefault(omit bool) { + e.omitEmpty = omit +} + // isValidStructPointer test if input value is a valid struct pointer. func isValidStructPointer(v reflect.Value) bool { return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct @@ -106,7 +117,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { // Encode non-slice types and custom implementations immediately. if encFunc != nil { value := encFunc(v.Field(i)) - if opts.Contains("omitempty") && isZero(v.Field(i)) { + if (e.omitEmpty || opts.Contains("omitempty")) && isZero(v.Field(i)) { continue } @@ -132,7 +143,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { } // Encode a slice. - if v.Field(i).Len() == 0 && opts.Contains("omitempty") { + if v.Field(i).Len() == 0 && (e.omitEmpty || opts.Contains("omitempty")) { continue } diff --git a/encoder_test.go b/encoder_test.go index 092f0de..58924dc 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -523,3 +523,32 @@ func TestRegisterEncoderWithPtrType(t *testing.T) { valExists(t, "DateStart", ss.DateStart.time.String(), vals) valExists(t, "DateEnd", "", vals) } + +func TestPtrsWithDefaultOmitEmpty(t *testing.T) { + type S1 struct { + S *string `schema:"s"` + B *bool `schema:"b"` + } + + ss := S1{} // all fields are nil + + // default encoder encodes ptrs as "null", which are not decodable + // so one has to use omitempty tags + encoder := NewEncoder() + + vals := map[string][]string{} + err := encoder.Encode(ss, vals) + + noError(t, err) + valsLength(t, 2, vals) + valExists(t, "s", "null", vals) + valExists(t, "b", "null", vals) + + // however, if encoder.SetOmitEmptyDefault(true) is used such fields should be ignored + vals = map[string][]string{} + encoder.SetOmitEmptyDefault(true) + err = encoder.Encode(ss, vals) + + noError(t, err) + valsLength(t, 0, vals) +}