覆盖json.Marshal使用的布局,以格式化time.Time。

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

Override the layout used by json.Marshal to format time.Time

问题

在Golang中,有没有办法使通用的encoding/json Marshal在编组time.Time字段时使用不同的布局?

基本上,我有这个结构体:

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

我想使用encoding/jsonMarshal函数将其编码为JSON,但我想使用自定义的布局。我想象中在某个地方调用了time.Format(layout),我想控制那个布局。

英文:

In Golang, is there a way to make the generic encoding/json Marshal to use a different layout when Marshaling the time.Time fields?

Basically I have this struct:

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

and I want to encoding to json using encdoding/json's Marshal function, but I want to use my custom layout, I imagine somewhere time.Format(layout) is being called, I want to control that layout,

答案1

得分: 15

根据zeebo的答案和对该答案的评论进行的讨论,得到了以下解决方案:

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结构体中。嵌入使得jsonTime结构体可以使用time.Time的所有方法,而无需进行显式类型转换(见 // 1)。

嵌入time.Time的一个缺点是它也会提升MarshalJSON方法,而encoding/json编组代码优先级比MarshalText方法更高,这是为了向后兼容性的原因(MarshalText是在Go 1.2中添加的,而MarshalJSON早于此)。因此,默认使用time.Time的格式,而不是由MarshalText提供的自定义格式。

为了解决这个问题,我们重写了jsonTime结构体的MarshalJSON方法。

英文:

As inspired by zeebo's answer and hashed out in the comments to that answer:

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

This solution embeds the time.Time into the jsonTime struct. Embedding promotes all of time.Time's methods to the jsonTime struct, allowing their use without explicit type conversion (see // 1).

Embedding a time.Time has the downside of also promoting the MarshalJSON method, which the encoding/json marshaling code prioritizes higher than the MarshalText method for backwards compatibility reasons (MarshalText was added in Go 1.2, MarshalJSON predates that). As a result the default time.Time format is used instead of a custom format provided by MarshalText.

To overcome this problem we override MarshalJSON for the jsonTime struct.

答案2

得分: 7

也许像这样的代码对你有用?

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

你可以创建一个自定义类型,并实现自己想要的MarshalText方法来显示时间。

英文:

Maybe something like this will work for you?

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

also available here: http://play.golang.org/p/D1kq5KrXQZ

Just make a custom type that implements MarshalText the way you want it to show up.

答案3

得分: 4

首先,我强烈建议不要使用除默认的RFC3339之外的时间格式。这是一个很好的时间格式,可以被许多编程语言解析,所以除非你需要使用其他格式来满足某个API的要求,否则最好使用默认格式。

但是,我曾经在使用其他人的API时解决过这个问题,所以这里有一个解决方案,它将大部分工作转移到了Marshal/Unmarshal步骤,并且给你留下了一个理想的数据结构:http://play.golang.org/p/DKaTbV2Zvl

英文:

First, I highly recommend against using a time format other than the default RFC3339. It's a good time format, and can be parsed by any number of languages, so unless you are needing a different format because somebody else's API requires it, it's probably best to use the default.

But, I've had to solve this problem in consuming other people's APIs, so here is one solution that shifts the bulk of the work to the Marshal/Unmarshal step, and leaves you with an ideal structure: http://play.golang.org/p/DKaTbV2Zvl

答案4

得分: 0

使用泛型,我找到了一种非常巧妙的方法来实现这一点,而无需手动初始化时间字段。

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) {
    // 在这里进行序列化操作
    stamp := fmt.Sprintf("\"%s\"", b.Layout())
    return []byte(stamp), nil
}

func (b *BaseDate[T]) UnmarshalJSON(data []byte) error {
    // 忽略 null,就像在主 JSON 包中一样。
    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)
}

我认为函数 String 是不必要的,但可以增加清晰度。这段代码之所以有效,是因为我可以为类型 Datetime、Datehour、Date 和 Hour 定义零值。

英文:

With generics I found a very hacky way to do this without initializing manually the time fields

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

And Time format is defined as follow

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

I think the function String is unnecessary but add clarity.
This works because I can define the zero value for the type Datetime, Datehour, Date and Hour.

huangapple
  • 本文由 发表于 2013年12月10日 00:16:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/20475321.html
匿名

发表评论

匿名网友

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

确定