自定义MarshalJSON导致结果不一致。

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

Inconsistent result with custom MarshalJSON

问题

在使用自定义的MarshalJSON函数时,我遇到了一些奇怪的行为。

考虑以下设置:

type ISODate struct {
	time.Time
}

var nilTime = (time.Time{}).UnixNano()

func (ct *ISODate) MarshalJSON() ([]byte, error) {
	if ct.Time.UnixNano() == nilTime {
		return []byte("null"), nil
	}
	return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format("2006-01-02"))), nil
}

然后,我期望的是,当我将一个ISODate编组为json时,得到的日期格式为2006-01-02。然而,这只在某些情况下发生。

当编组像这样包装的ISODate时:

var d ISODate = ISODate{Time: time.Date(9999, time.December, 31, 23, 59, 59, 999999999, time.UTC)}
m1 := map[int]ISODate{1: d}
m2 := map[int][]ISODate{1: []ISODate{d}}

b, err := json.Marshal(m1)
b, err = json.Marshal(m2)

它给出以下结果:

{"1":"9999-12-31T23:59:59.999999999Z"}
{"1":["9999-12-31"]}

我期望的是两个ISODate都像第二个例子那样。

我的问题是,我的实现中是否存在错误,还是这种行为是预期的?

英文:

I have encountered some rather strange behaviour when working with custom MarshalJSON functions in go.

Consider the following setup:

type ISODate struct {
	time.Time
}

var nilTime = (time.Time{}).UnixNano()

func (ct *ISODate) MarshalJSON() ([]byte, error) {
	if ct.Time.UnixNano() == nilTime {
		return []byte("null"), nil
	}
	return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format("2006-01-02"))), nil
}

What I would then expect is that when I marshal an ISODate to json I get a date of the format 2006-01-02. This however only happens in some of the cases.

When marshalling ISODates wrapped like so:

var d ISODate = ISODate{Time: time.Date(9999, time.December, 31, 23, 59, 59, 999999999, time.UTC)}
m1 := map[int]ISODate{1: d}
m2 := map[int][]ISODate{1: []ISODate{d}}
	
b, err := json.Marshal(m1)
b, err = json.Marshal(m2)

It gives the following results:

{"1":"9999-12-31T23:59:59.999999999Z"}
{"1":["9999-12-31"]}

Try it on Go Playground

What I would expect is for both ISODates to look like the second case.

My question is whether there is some error in my implementation or if this behaviour is expected?

答案1

得分: 1

在第一种情况下,你的自定义编组器没有被调用,而是调用了默认的基于反射的json.Marshaler

https://play.golang.org/p/fGxTjKbsM5X

要修复这个问题,将你的函数签名更改为非指针接收器:

// func (ct *ISODate) MarshalJSON() ([]byte, error)  { /* ... */ }
func (ct ISODate) MarshalJSON() ([]byte, error) { /* ... */ }

https://play.golang.org/p/rgreJol19FO


编辑

你可以强制编组器使用指针接收器,并且在不需要特殊的nilTime值的情况下处理nil情况:

func (ct *ISODate) MarshalJSON() ([]byte, error) {
    // 可以在nil值上调用方法
    if ct == nil {
        return []byte("null"), nil
    }
    return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format("2006-01-02"))), nil
}

https://play.golang.org/p/dy2XxS9hSYN

英文:

In the first case your custom marshaller is not getting called - but instead the default reflection-based json.Marshaler:

https://play.golang.org/p/fGxTjKbsM5X

To fix, change your function signature to a non-pointer receiver:

// func (ct *ISODate) MarshalJSON() ([]byte, error)  { /* ... */ }
func (ct ISODate) MarshalJSON() ([]byte, error) { /* ... */ }

https://play.golang.org/p/rgreJol19FO


Edit:

you can coerce the marshaler to use a pointer receiver - and also handle the nil case without the need for a special nilTime value:

func (ct *ISODate) MarshalJSON() ([]byte, error) {
    // a method can be called on a nil value
	if ct == nil {
		return []byte("null"), nil
	}
	return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format("2006-01-02"))), nil
}

https://play.golang.org/p/dy2XxS9hSYN

huangapple
  • 本文由 发表于 2021年6月8日 20:19:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/67886791.html
匿名

发表评论

匿名网友

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

确定