The post describes how to unmarshal time.Duration which is not supported by the default JSON unmarshaler, but the example can be used to unmarshal any custom type.

Here is the JSON we’re unmarshaling:

1
2
3
4
{
	"addr": "https://example.com",
	"timeout": "5s",
}

We’d like to convert the 5s value to a time.Duration value corresponding to 5 seconds and get the JSON to unmarshal in the following data structure:

1
2
3
4
type connection struct {
	Addr 	string		`json:"addr"`
	Timeout time.Duration 	`json:"timeout"`
}

One way to do it is to implement the Unmarshaler interface and handle the conversion in our UnmarshalJSON function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Unmarshaler is the interface implemented by types  
// that can unmarshal a JSON description of themselves.  
// The input can be assumed to be a valid encoding of  
// a JSON value. UnmarshalJSON must copy the JSON data  
// if it wishes to retain the data after returning.  
//  
// By convention, to approximate the behavior of Unmarshal itself,  
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.  
type Unmarshaler interface {  
   UnmarshalJSON([]byte) error  
}

Below is the complete example. We unmarshal the JSON into a temprary structure where 5s is a standard string. Then we convert the string to time.Duration vaue and set it in our structure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package mypackage

import(
	"json/encoding"
	"time"
)

type connection struct {
	Addr 	string 		  `json:"addr"`
	Timeout time.Duration `json:"timeout"`
}

func (c *connection) UmarshalJSON(data []byte) (err error) {
	var tmp struct {
		Addr 	string
		Timeout string
	}
	if err = json.Unmarshal(data, &tmp); err != nil {
		return err
	}

	c.Addr = tmp.Addr
	c.Timeout, err = time.ParseDuration(tmp.Timeout)
	return err
}