解析不符合 RFC 3339 格式的时间的 JSON

huangapple go评论81阅读模式
英文:

json unmarshal time that isn't in RFC 3339 format

问题

在Go语言中,处理不同时间格式的反序列化的适当方式是什么?encoding/json包似乎只接受RFC 3339格式,非常死板。我可以将其反序列化为字符串,然后将其转换为RFC 3339格式并进行解组,但我不太想这样做。有更好的解决方案吗?

英文:

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?

答案1

得分: 75

你需要在自定义类型上实现json.Marshaler/json.Unmarshaler接口,并使用该类型替代原来的类型。以下是一个示例:

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.IsZero() {
        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.IsZero()
}

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())
}

编辑:添加了CustomTime.IsSet()函数,用于检查时间是否已设置,以供将来参考。

英文:

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.IsZero() {
	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.IsZero()
}

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.

答案2

得分: 37

编码/解码是由time.Time自身完成的,在MarshalJSONUnmarshalJSON方法中。您可以创建自己的time.Time类型并覆盖这些函数,以便根据您的需求与JSON一起使用。

type Time struct {
    time.Time
}

// 无论如何都返回time.Now()!
func (t *Time) UnmarshalJSON(b []byte) error {
    // 您现在可以根据需要彻底解析b

    *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)
}
英文:

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)
}

huangapple
  • 本文由 发表于 2014年8月2日 04:11:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/25087960.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定