How to unmarshal JSON into durations?

Just to extend the previous answer. There is another way (very close to Tim's)

type Duration time.Duration 

func (d Duration) MarshalJSON() ([]byte, error) {
    return json.Marshal(time.Duration(d).String())
}

func (d *Duration) UnmarshalJSON(b []byte) error {
    var v interface{}
    if err := json.Unmarshal(b, &v); err != nil {
        return err
    }
    switch value := v.(type) {
    case float64:
        *d = Duration(time.Duration(value))
        return nil
    case string:
        tmp, err := time.ParseDuration(value)
        if err != nil {
            return err
        }
        *d = Duration(tmp)
        return nil
    default:
        return errors.New("invalid duration")
    }
}

The lack of JSON marshaling and unmarshaling methods on time.Duration was an unfortunate oversight. This should hopefully be resolved in Go2 (see issue #10275).

You can, however, define your own type around time.Duration that supports marshaling to the string representation of the duration and unmarshaling from either the numeric or string representations. Here is an example of such an implementation:

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "time"
)

type Duration struct {
    time.Duration
}

func (d Duration) MarshalJSON() ([]byte, error) {
    return json.Marshal(d.String())
}

func (d *Duration) UnmarshalJSON(b []byte) error {
    var v interface{}
    if err := json.Unmarshal(b, &v); err != nil {
        return err
    }
    switch value := v.(type) {
    case float64:
        d.Duration = time.Duration(value)
        return nil
    case string:
        var err error
        d.Duration, err = time.ParseDuration(value)
        if err != nil {
            return err
        }
        return nil
    default:
        return errors.New("invalid duration")
    }
}

type Message struct {
    Elapsed Duration `json:"elapsed"`
}

func main() {
    msgEnc, err := json.Marshal(&Message{
        Elapsed: Duration{time.Second * 5},
    })
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s\n", msgEnc)

    var msg Message
    if err := json.Unmarshal([]byte(`{"elapsed": "1h"}`), &msg); err != nil {
        panic(err)
    }
    fmt.Printf("%#v\n", msg)
}

https://play.golang.org/p/Zm6hpNR-ZJ2