【问题标题】:How to parse non standard time format from json如何从json解析非标准时间格式
【发布时间】:2017-12-31 09:09:30
【问题描述】:

假设我有以下 json

{
    name: "John",
    birth_date: "1996-10-07"
}

我想把它解码成下面的结构

type Person struct {
    Name string `json:"name"`
    BirthDate time.Time `json:"birth_date"`
}

喜欢这个

person := Person{}

decoder := json.NewDecoder(req.Body);

if err := decoder.Decode(&person); err != nil {
    log.Println(err)
}

这给了我错误parsing time ""1996-10-07"" as ""2006-01-02T15:04:05Z07:00"": cannot parse """ as "T"

如果我要手动解析它,我会这样做

t, err := time.Parse("2006-01-02", "1996-10-07")

但是当时间值来自 json 字符串时我如何让解码器以上述格式解析它?

【问题讨论】:

标签: go time


【解决方案1】:

这是您需要实现自定义编组和解组功能的情况。

UnmarshalJSON(b []byte) error { ... }

MarshalJSON() ([]byte, error) { ... }

按照 json package 的 Golang 文档中的示例,您会得到类似的内容:

// First create a type alias
type JsonBirthDate time.Time

// Add that to your struct
type Person struct {
    Name string `json:"name"`
    BirthDate JsonBirthDate `json:"birth_date"`
}

// Implement Marshaler and Unmarshaler interface
func (j *JsonBirthDate) UnmarshalJSON(b []byte) error {
    s := strings.Trim(string(b), "\"")
    t, err := time.Parse("2006-01-02", s)
    if err != nil {
        return err
    }
    *j = JsonBirthDate(t)
    return nil
}
    
func (j JsonBirthDate) MarshalJSON() ([]byte, error) {
    return json.Marshal(time.Time(j))
}

// Maybe a Format function for printing your date
func (j JsonBirthDate) Format(s string) string {
    t := time.Time(j)
    return t.Format(s)
}

【讨论】:

  • 对,对于 UnmarshalJSON 函数,OP 可以根据需要支持的不同格式的数量添加多个 time.Parse 尝试。我相信time.RFC3339 的格式是默认解析器,更多格式可以在docs中找到
  • 当然,当你有自定义的 un/marshal 函数时,你应该尽量覆盖所有可能的情况。
  • *j = JB(t) 行中的 JB 是什么?
  • 因为你有一个类型别名,你需要转换它。阅读此内容以及文档的相应链接:stackoverflow.com/questions/19577423/…
  • MarshalJSON() 方法会导致堆栈溢出恐慌,因为它间接调用了自己。
【解决方案2】:

如果有很多结构,而您只是实现自定义编组和解组功能,那么要做的工作量很大。您可以使用其他库代替,例如 json-iterator 扩展 jsontime:

import "github.com/liamylian/jsontime"

var json = jsontime.ConfigWithCustomTimeFormat

type Book struct {
    Id        int           `json:"id"`
    UpdatedAt *time.Time    `json:"updated_at" time_format:"sql_date" time_utc:"true"`
    CreatedAt time.Time     `json:"created_at" time_format:"sql_datetime" time_location:"UTC"`
}

【讨论】:

    【解决方案3】:

    我写了一个包来处理yyyy-MM-ddyyyy-MM-ddThh:mm:ss 日期https://github.com/a-h/date

    它使用上面答案中的类型别名方法,然后通过一些更改实现 MarshalJSONUnmarshalJSON 函数。

    // MarshalJSON outputs JSON.
    func (d YYYYMMDD) MarshalJSON() ([]byte, error) {
        return []byte("\"" + time.Time(d).Format(formatStringYYYYMMDD) + "\""), nil
    }
    
    // UnmarshalJSON handles incoming JSON.
    func (d *YYYYMMDD) UnmarshalJSON(b []byte) (err error) {
        if err = checkJSONYYYYMMDD(string(b)); err != nil {
            return
        }
        t, err := time.ParseInLocation(parseJSONYYYYMMDD, string(b), time.UTC)
        if err != nil {
            return
        }
        *d = YYYYMMDD(t)
        return
    }
    

    在正确的时区进行解析很重要。我的代码假定为 UTC,但您可能出于某种原因希望使用计算机的时区。

    我还发现涉及使用 time.Parse 函数的解决方案将 Go 的内部机制泄漏为客户认为没有帮助的错误消息,例如:cannot parse "sdfdf-01-01" as "2006"。仅当您知道服务器是用 Go 编写的并且2006 是示例日期格式时才有用,因此我输入了更具可读性的错误消息。

    我还实现了Stringer 接口,以便它可以漂亮地打印在日志或调试消息中。

    【讨论】:

      猜你喜欢
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 2012-01-29
      • 2011-03-09
      • 2012-02-12
      • 2014-01-13
      • 2017-04-11
      • 1970-01-01
      相关资源
      最近更新 更多