From time to time, you’ll want to serialise/deserialise a struct whose default serialisation seems counter-intuitive. Take for example, the JSON serlisation of time.Duration
:
json.NewEncoder(os.Stdout).Encode(time.Hour)
// result: 3600000000000
I don’t event think science knows what this number is.
Asking people to configure 1 hour as “3600000000000” is not only cruel, it’s asking for trouble; miss a zero and you’ve had it.
A much friendlier and more natural alternative is to allow for the confguration of time.Duration
as you’re used to seeing it appear (1h
, 1m30s
etc). To acheive this, you’ll need to perform some custom JSON marshaling and unmarshaling.
Here’s a copy-paste-friendly example:
Step 1 - create a struct to house the time.Duration
and receive the custom marshal operations.
type ConfigDuration struct {
time.Duration
}
func (d *ConfigDuration) UnmarshalJSON(b []byte) (err error) {
d.Duration, err = time.ParseDuration(strings.Trim(string(b), `"`))
return
}
func (d ConfigDuration) MarshalJSON() (b []byte, err error) {
return []byte(fmt.Sprintf(`"%s"`, d.String())), nil
}
Step 2 - create a struct that consumes the new ConfigDuration
struct:
type ServerConfig struct {
ReadTimeout ConfigDuration `json:"readTimeout"`
}
Step 3 - Unmarshal some JSON and access the time.Duration
:
config := ServerConfig{}
json.Unmarshal([]byte(`{"readTimeout":"10s"}`), &config)
fmt.Println(config.ReadTimeout.Duration)
// result: 10s