Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

json unmarshal time that isn't in RFC 3339 format

Tags:

json

go

What is the appropriate way to handle deserialization of different time formats in Go? The encoding/json package seems to be entirely rigid in only accepted RFC 3339. I can deserialize into a string, transform that into RFC 3339 and then unmarshal it but I don't really want to do that. Any better solutions?

like image 335
evanmcdonnal Avatar asked Aug 01 '14 20:08

evanmcdonnal


2 Answers

You will have to implement the json.Marshaler / json.Unmarshaler interfaces on a custom type and use that instead, an example:

type CustomTime struct {
    time.Time
}

const ctLayout = "2006/01/02|15:04:05"

func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
    s := strings.Trim(string(b), "\"")
    if s == "null" {
       ct.Time = time.Time{}
       return
    }
    ct.Time, err = time.Parse(ctLayout, s)
    return
}

func (ct *CustomTime) MarshalJSON() ([]byte, error) {
  if ct.Time.UnixNano() == nilTime {
    return []byte("null"), nil
  }
  return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ctLayout))), nil
}

var nilTime = (time.Time{}).UnixNano()
func (ct *CustomTime) IsSet() bool {
    return ct.UnixNano() != nilTime
}

type Args struct {
    Time CustomTime
}

var data = `
    {"Time": "2014/08/01|11:27:18"}
`

func main() {
    a := Args{}
    fmt.Println(json.Unmarshal([]byte(data), &a))
    fmt.Println(a.Time.String())
}

edit: added CustomTime.IsSet() to check it was actually set or not, for future reference.

like image 153
OneOfOne Avatar answered Oct 26 '22 00:10

OneOfOne


The encoding/decoding is done by time.Time itself, in the MarshalJSON and UnamrshalJSON methods. You could create your own time.Time type and override those functions to work with the json however you want.

type Time struct {
    time.Time
}

// returns time.Now() no matter what!
func (t *Time) UnmarshalJSON(b []byte) error {
    // you can now parse b as thoroughly as you want

    *t = Time{time.Now()}
    return nil
}

type Config struct {
    T Time
}

func main() {
    c := Config{}

    json.Unmarshal([]byte(`{"T": "bad-time"}`), &c)

    fmt.Printf("%+v\n", c)
}
like image 38
JimB Avatar answered Oct 26 '22 01:10

JimB