如何从golang中的json解析非标准时间格式?

时间:2017-07-25 12:28:44

标签: go time

让我说我有以下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字符串我如何让解码器以上述格式解析它?

3 个答案:

答案 0 :(得分:13)

当你需要实现自定义编组和解组函数时就是这种情况。

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"`
}

// imeplement Marshaler und Unmarshalere 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(j)
}

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

答案 1 :(得分:2)

如果有很多结构,你只需要实现自定义编组和解组函数,那么这需要做很多工作。您可以改用另一个lib,例如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"`
}

答案 2 :(得分:1)

我写了一个程序包,用于处理https://github.com/a-h/date处的yyyy-MM-ddyyyy-MM-ddThh:mm:ss日期

它在上面的答案中使用类型别名方法,然后通过一些改动实现了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接口,以便在日志或调试消息中漂亮地显示它。