Go语言 重写json.marshal使用的布局以格式化time.time

f1tvaqid  于 2023-03-10  发布在  Go
关注(0)|答案(4)|浏览(201)

在Golang中,有没有办法让通用的encoding/json封送在封送time.Time字段时使用不同的布局?
基本上我有这样的结构:

s := {"starttime":time.Now(), "name":"ali"}

我想用encdoding/jsonMarshal函数编码成json,但是我想用我的自定义布局,我想象time.Format(layout)在某个地方被调用,我想控制那个布局,

omtl5h9j

omtl5h9j1#

正如受到齐博回答的启发,并在评论中对该回答进行了详细阐述:
http://play.golang.org/p/pUCBUgrjZC

package main

import "fmt"
import "time"
import "encoding/json"

type jsonTime struct {
    time.Time
    f string
}

func (j jsonTime) format() string {
    return j.Time.Format(j.f)
}

func (j jsonTime) MarshalText() ([]byte, error) {
    return []byte(j.format()), nil
}

func (j jsonTime) MarshalJSON() ([]byte, error) {
    return []byte(`"` + j.format() + `"`), nil
}

func main() {
    jt := jsonTime{time.Now(), time.Kitchen}
    if jt.Before(time.Now().AddDate(0, 0, 1)) { // 1
        x := map[string]interface{}{
            "foo": jt,
            "bar": "baz",
        }
        data, err := json.Marshal(x)
        if err != nil {
            panic(err)
        }
        fmt.Printf("%s", data)
    }
}

这个解决方案将time.Time嵌入到jsonTime结构体中。嵌入将time.Time的所有方法提升到jsonTime结构体中,允许在没有显式类型转换的情况下使用它们(参见// 1)。
嵌入time.Time的缺点是也会提升MarshalJSON方法,出于向后兼容的原因,encoding/json封送代码将其优先级设置为高于MarshalText方法(MarshalText是在Go语言1.2中添加的,而MarshalJSON早于MarshalText方法)。因此,使用默认的time.Time格式,而不是MarshalText提供的自定义格式。
为了克服这个问题,我们为jsonTime结构体重写MarshalJSON。

gijlo24d

gijlo24d2#

也许这样的东西对你有用?

package main

import "fmt"
import "time"
import "encoding/json"

type jsonTime struct {
t time.Time
f string
}

func (j jsonTime) MarshalText() ([]byte, error) {
return []byte(j.t.Format(j.f)), nil
}

func main() {
x := map[string]interface{}{
    "foo": jsonTime{t: time.Now(), f: time.Kitchen},
    "bar": "baz",
}
data, err := json.Marshal(x)
if err != nil {
    panic(err)
}
fmt.Printf("%s", data)
}

也可从以下网址获得:http://play.golang.org/p/D1kq5KrXQZ
只需创建一个自定义类型,该类型按照您希望的方式实现MarshalText。

nnvyjq4y

nnvyjq4y3#

首先,我强烈建议不要使用默认RFC 3339以外的时间格式。它是一种很好的时间格式,可以由许多语言解析,因此除非您因为其他人的API需要它而需要不同的格式,否则最好使用默认格式。
但是,我不得不在使用其他人的API时解决这个问题,所以这里有一个解决方案,它将大部分工作转移到编组/解编组步骤,并为您留下一个理想的结构:http://play.golang.org/p/DKaTbV2Zvl

2izufjch

2izufjch4#

使用泛型时,我发现了一种不需要手动初始化时间字段就可以实现这一点的非常好的方法

type BaseDate[T TimeFormat] struct {
    time.Time
}

func (b *BaseDate[T]) Layout() string {
    format := *new(T)
    return format.String()
}

func (b *BaseDate[T]) Format() string {
    return b.Time.Format(b.Layout())
}

func (b *BaseDate[T]) MarshalJSON() ([]byte, error) {
    //do your serializing here
    stamp := fmt.Sprintf("\"%s\"", b.Layout())
    return []byte(stamp), nil
}

func (b *BaseDate[T]) UnmarshalJSON(data []byte) error {
    // Ignore null, like in the main JSON package.
    str := strings.Trim(string(data), "\"")
    if str == "null" || str == "" {
        return nil
    }
    tt, err := time.Parse(b.Layout(), str)
    b.Time = tt
    return err
}

func Now[T TimeFormat]() BaseDate[T] {
    return BaseDate[T]{Time: time.Now()}
}

时间格式定义如下

type TimeFormat interface {
    Datetime | Datehour | Date | Hour
    String() string
}

type Datetime string
type Datehour string
type Date string
type Hour string

const (
    dateTime Datetime = "2006-01-02T15:04:05"
    dateHour Datehour = "2006-01-02T15:04"
    date     Date     = "2006-01-02"
    hour Hour = "15:04"
)

func (d Datetime) String() string {
    return string(DateTime)
}

func (d Datehour) String() string {
    return string(DateHour)
}
func (d Date) String() string {
    return string(date)
}
func (d Hour) String() string {
    return string(hour)
}

我认为字符串函数是不必要的,但增加了清晰度。这是有效的,因为我可以为类型Datetime,Datehour,Date和Hour定义零值。

相关问题