解析 Revel 中的 JSON 日期时间

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

Parsing a json datetime in revel

问题

我正在尝试从JSON请求中解析一个Datetime。请求的格式如下所示:

  1. {
  2. "startedAt": "2017-06-01 10:39",
  3. ...
  4. }

被解码为以下结构体:

  1. type MyStruct struct {
  2. StartedAt time.Time `json:"startedAt" bson:"startedAt"`
  3. ...
  4. }

解码行如下所示:

  1. json.NewDecoder(c.Request.Body).Decode(&MyStruct)

Revel返回以下错误:

  1. interface conversion: error is *time.ParseError, not *errors.Error

根据revel的文档(https://revel.github.io/manual/parameters.html),SQL标准的日期时间格式2006-01-02、2006-01-02 15:04是内置的。

在同一文档中,他们还说可以追加格式,如下所示:

  1. func init() {
  2. revel.TimeFormats = append(revel.TimeFormats, "01/02/2006")
  3. }

为了验证格式是否在数组中,我尝试了以下代码:

  1. func init() {
  2. revel.TimeFormats = append(revel.TimeFormats, "2016-06-01 12:12")
  3. }

在绝望之下,我尝试以revel返回JSON时间的相同格式提交:

  1. {
  2. "startedAt": "2017-06-22T12:22:16.265618819-05:00",
  3. ...
  4. }

目前我不确定该怎么办。有人能够成功解析Datetime吗?


更新:我尝试了RickyA的解决方案,但现在出现了解析错误。

  1. parsing time ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon, 02 Jan 2006 15:04:05 -0700": cannot parse ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon"

更奇怪的是,我实现了一个小技巧来解决这个问题。我将请求时间字段更改为字符串,并给它一个ToX函数来进行转换。该函数有效,但当它移动到UnmarshalJSON函数中时却失败了。

此外,我无法确定这是否是一个错误:

  1. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  2. fmt.Println(time.RFC1123Z)
  3. fmt.Println(string(b))
  4. fmt.Println(reflect.TypeOf(time.RFC1123Z))
  5. fmt.Println(reflect.TypeOf(string(b)))
  6. ...

这将输出:

  1. Mon, 02 Jan 2006 15:04:05 -0700
  2. "Mon, 02 Jan 2006 15:04:05 -0700"
  3. string
  4. string

请注意,只有一个字符串带有双引号。这让我相信,传递给UnmarshalJSON的字节数组中包含双引号。


最终更新:我相当确定双引号是有意的,从某种意义上讲,这是有道理的。UnmarshalJSON将从':'到','之间的所有内容传递,包括双引号。我猜这样做是为了支持整数和布尔值。我不喜欢这个解决方案,但这是我修复它的方法:

  1. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  2. var err error
  3. sTime := string(b)
  4. sTime = strings.TrimSuffix(sTime, "\"")
  5. sTime = strings.TrimPrefix(sTime, "\"")
  6. t.Time, err = time.Parse(time.RFC1123Z, sTime)
  7. ...
英文:

I'm attempting to parse a Datetime in revel from a json request. The request looks something like this:

  1. {
  2. "startedAt": "2017-06-01 10:39",
  3. ...
  4. }

The struct it's being decoded into looks like this:

  1. type MyStruct struct {
  2. StartedAt time.Time `json:"startedAt" bson:"startedAt"`
  3. ...
  4. }

The decode line looks like this:

  1. json.NewDecoder(c.Request.Body).Decode(&MyStruct)

Revel returns this error:

  1. interface conversion: error is *time.ParseError, not *errors.Error

According to revel's documentation here, https://revel.github.io/manual/parameters.html

  1. The SQL standard date time formats of 2006-01-02, 2006-01-02 15:04 are built in.

In the same document, they also say you can append formats like this:

  1. func init() {
  2. revel.TimeFormats = append(revel.TimeFormats, "01/02/2006")
  3. }

To verify the format was in the array, I tried this:

  1. func init() {
  2. revel.TimeFormats = append(revel.TimeFormats, "2016-06-01 12:12")
  3. }

In a final act of desperation I tried submitting it in the same format that revel will return json times in:

  1. {
  2. "startedAt": "2017-06-22T12:22:16.265618819-05:00",
  3. ...
  4. }

At this point I'm not sure where to go with this. Has anyone been able to get revel to parse a Datetime?


Update: I tried RickyA's solution below, but now there is a parsing error.

  1. parsing time ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon, 02 Jan 2006 15:04:05 -0700": cannot parse ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon"

What's even stranger is that I implemented a bit of a hack to get this working in the interrum. I changed the request time field to a string, and gave it a ToX function which converted it. That function works, but when it's moved into the UnmarshalJSON function it fails.

Also, I can't tell if this is a bug or not:

  1. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  2. fmt.Println(time.RFC1123Z)
  3. fmt.Println(string(b))
  4. fmt.Println(reflect.TypeOf(time.RFC1123Z))
  5. fmt.Println(reflect.TypeOf(string(b)))
  6. ...

This outputs this:

  1. Mon, 02 Jan 2006 15:04:05 -0700
  2. "Mon, 02 Jan 2006 15:04:05 -0700"
  3. string
  4. string

Notice that only 1 of the string has double quotes. This is leading me to believe that some how the byte array passed to UnmarshalJSON has double quotes within its string.


Final Update: So I'm pretty sure the double quotes are intentional, and in a way it makes sense. The UnmarshalJSON is passing everything from the ':' through the ',' which includes the double quotes. I'm guessing this is done so that it can also support integers and booleans. I don't like the solution, but this is how I fixed it:

  1. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  2. var err error
  3. sTime := string(b)
  4. sTime = strings.TrimSuffix(sTime, "\"")
  5. sTime = strings.TrimPrefix(sTime, "\"")
  6. t.Time, err = time.Parse(time.RFC1123Z, sTime)
  7. ...

答案1

得分: 3

我遇到了同样的问题,最后创建了一个自定义类型来处理time.Time

  1. package genericfields
  2. import (
  3. "encoding/json"
  4. "time"
  5. "strings"
  6. "gopkg.in/mgo.v2/bson"
  7. )
  8. // ANYTIME //
  9. // AnyTime接受任何时间格式进行反序列化//
  10. type AnyTime struct{ time.Time }
  11. func (t AnyTime) MarshalJSON() ([]byte, error) {
  12. return json.Marshal(t.Time)
  13. }
  14. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  15. err := json.Unmarshal(b, &t.Time)
  16. if err != nil { //假设输入的时间没有时区信息
  17. bstr := strings.Trim(string(b), `"`)
  18. t.Time, err = time.Parse("2006-01-02T15:04:05", bstr)
  19. if err != nil {
  20. return err //TODO 添加更多尝试的时间格式
  21. }
  22. }
  23. return nil
  24. }
  25. func (t AnyTime) GetBSON() (interface{}, error) {
  26. return t.Time, nil
  27. }
  28. func (t *AnyTime) SetBSON(raw bson.Raw) error {
  29. var tm time.Time
  30. err := raw.Unmarshal(&tm)
  31. if err != nil {
  32. return err
  33. }
  34. t.Time = tm.UTC()
  35. return nil
  36. }
  37. func (t AnyTime) ToTime() time.Time {
  38. return t.Time
  39. }

用法:

  1. type MyStruct struct {
  2. StartedAt genericfields.AnyTime `json:"startedAt" bson:"startedAt"`
  3. ...
  4. }

你可能需要调整time.Parse的输入格式。

英文:

I had the same problem and ended up creating a custom type for time.Time

  1. package genericfields
  2. import (
  3. "encoding/json"
  4. "time"
  5. "strings"
  6. "gopkg.in/mgo.v2/bson"
  7. )
  8. // ANYTIME //
  9. // AnyTime accepts any time format for its unmarshaling //
  10. type AnyTime struct{ time.Time }
  11. func (t AnyTime) MarshalJSON() ([]byte, error) {
  12. return json.Marshal(t.Time)
  13. }
  14. func (t *AnyTime) UnmarshalJSON(b []byte) error {
  15. err := json.Unmarshal(b, &t.Time)
  16. if err != nil { //assume non tz time input
  17. bstr := strings.Trim(string(b), `"`)
  18. t.Time, err = time.Parse("2006-01-02T15:04:05", bstr)
  19. if err != nil {
  20. return err //TODO add more formats to try
  21. }
  22. }
  23. return nil
  24. }
  25. func (t AnyTime) GetBSON() (interface{}, error) {
  26. return t.Time, nil
  27. }
  28. func (t *AnyTime) SetBSON(raw bson.Raw) error {
  29. var tm time.Time
  30. err := raw.Unmarshal(&tm)
  31. if err != nil {
  32. return err
  33. }
  34. t.Time = tm.UTC()
  35. return nil
  36. }
  37. func (t AnyTime) ToTime() time.Time {
  38. return t.Time
  39. }

Usage:

  1. type MyStruct struct {
  2. StartedAt genericfields.AnyTime `json:"startedAt" bson:"startedAt"`
  3. ...
  4. }

You might need to tweak the input for time.Parse somewhat.

huangapple
  • 本文由 发表于 2017年6月23日 01:24:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/44705817.html
匿名

发表评论

匿名网友

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

确定