Support zero values of structs with omitempty in encoding/json and encoding/xml.
This bites people a lot, especially with time.Time. Open bugs include #4357 (which has many dups) and #10648. There may be others.
Proposal
Check for zero struct values by adding an additional case to the isEmptyValue function:
case reflect.Struct:
return reflect.Zero(v.Type()).Interface() == v.Interface()
This will solve the vast majority of cases.
(Optional) Introduce a new encoding.IsZeroer interface, and use this to check for emptiness:
Update: I am dropping this part of the proposal, see below.
type IsZeroer interface {
IsZero() bool
}
Visit this playground link and note that the unmarshaled time.Time value does not have a nil Location field. This prevents the reflection-based emptiness check from working. IsZero() already exists on time.Time, has the correct semantics, and has been adopted as a convention by Go code outside the standard library.
An additional check can be added to the isEmptyValue() functions before checking the value's Kind:
if z, ok := v.Interface().(encoding.IsZeroer); ok {
return z.IsZero()
}
Compatibility
The encoding.IsZeroer interface could introduce issues with existing non-struct types that may have implemented IsZero() without consideration of omitempty. If this is undesirable, the encoding.IsZeroer interface check could be moved only within the struct case:
case reflect.Struct:
val := v.Interface()
if z, ok := val.(encoding.IsZeroer); ok {
return z.IsZero()
}
return reflect.Zero(v.Type()).Interface() == val
Otherwise, this change is backward-compatible with existing valid uses of omitempty. Users who have applied omitempty to struct fields incorrectly will get their originally intended behavior for free.
Implementation
I (@joeshaw) have implemented and tested this change locally, and will send the CL when the Go 1.6 tree opens.
Support zero values of structs with omitempty in encoding/json and encoding/xml.
This bites people a lot, especially with
time.Time. Open bugs include #4357 (which has many dups) and #10648. There may be others.Proposal
Check for zero struct values by adding an additional case to the
isEmptyValuefunction:This will solve the vast majority of cases.
(Optional) Introduce a new
encoding.IsZeroerinterface, and use this to check for emptiness:Update: I am dropping this part of the proposal, see below.
Visit this playground link and note that the unmarshaled
time.Timevalue does not have anilLocationfield. This prevents the reflection-based emptiness check from working.IsZero()already exists ontime.Time, has the correct semantics, and has been adopted as a convention by Go code outside the standard library.An additional check can be added to the
isEmptyValue()functions before checking the value'sKind:Compatibility
The
encoding.IsZeroerinterface could introduce issues with existing non-struct types that may have implementedIsZero()without consideration ofomitempty. If this is undesirable, theencoding.IsZeroerinterface check could be moved only within the struct case:Otherwise, this change is backward-compatible with existing valid uses of
omitempty. Users who have appliedomitemptyto struct fields incorrectly will get their originally intended behavior for free.Implementation
I (@joeshaw) have implemented and tested this change locally, and will send the CL when the Go 1.6 tree opens.