英文:
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"]}
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论