JSON omitempty With time.Time Field
You may define you self Time type for custom marshal format, and use it everywhere instead time.Time
https://play.golang.org/p/C8nIR1uZAok
package main
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
type MyTime struct {
*time.Time
}
func (t MyTime) MarshalJSON() ([]byte, error) {
return []byte(t.Format("\"" + time.RFC3339 + "\"")), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// The time is expected to be a quoted string in RFC 3339 format.
func (t *MyTime) UnmarshalJSON(data []byte) (err error) {
// by convention, unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
if bytes.Equal(data, []byte("null")) {
return nil
}
// Fractional seconds are handled implicitly by Parse.
tt, err := time.Parse("\""+time.RFC3339+"\"", string(data))
*t = MyTime{&tt}
return
}
func main() {
t := time.Now()
d, err := json.Marshal(MyTime{&t})
fmt.Println(string(d), err)
var mt MyTime
json.Unmarshal(d, &mt)
fmt.Println(mt)
}
The omitempty
tag option does not work with time.Time
as it is a struct
. There is a "zero" value for structs, but that is a struct value where all fields have their zero values. This is a "valid" value, so it is not treated as "empty".
But by simply changing it to a pointer: *time.Time
, it will work (nil
pointers are treated as "empty" for json marshaling/unmarshaling). So no need to write custom Marshaler
in this case:
type MyStruct struct {
Timestamp *time.Time `json:",omitempty"`
Date *time.Time `json:",omitempty"`
Field string `json:",omitempty"`
}
Using it:
ts := time.Date(2015, 9, 18, 0, 0, 0, 0, time.UTC)
ms := MyStruct{
Timestamp: &ts,
Field: "",
}
Output (as desired):
{"Timestamp":"2015-09-18T00:00:00Z"}
Try it on the Go Playground.
If you can't or don't want to change it to a pointer, you can still achieve what you want by implementing a custom Marshaler
and Unmarshaler
. If you do so, you can use the Time.IsZero()
method to decide if a time.Time
value is the zero value.
As a follow up to icza's answer here is a custom marshaller that omits an empty date field but keeps the rest of the fields unchanged.
func (ms *MyStruct) MarshalJSON() ([]byte, error) {
type Alias MyStruct
if ms.Timestamp.IsZero() {
return json.Marshal(&struct {
Timestamp int64 `json:",omitempty"`
*Alias
}{
Timestamp: 0,
Alias: (*Alias)(ms),
})
} else {
return json.Marshal(&struct {
*Alias
}{
Alias: (*Alias)(ms),
})
}
}
This borrows from http://choly.ca/post/go-json-marshalling/
The OPs case has two time fields which would make it much more complicated. (you'd have to check for neither, either and both being empty!)
There may be better ways to achieve this, so comments are welcome.