英文:
Multiple custom time formats in go for JSON Marshall
问题
我有一个用于在Go中编组和解组自定义时间格式的实用函数,对于一个格式字符串它可以正常工作,并且在JSON模型中使用如下:
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
Duid string `json:"duid"`
IntervalCount int32 `json:"intervalCount"`
Intervals []int32 `json:"intervals"`
RequestId string `json:"requestId"`
RequestTimestamp time.Time `json:"requestTimestamp"`
TradingDate utils.CustomTime `json:"tradingDate"`
Unit string `json:"unit"`
RebidExplanation string `json:"rebidexplanation"`
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
}
实用函数如下:
package utils
import (
"database/sql/driver"
"fmt"
"strings"
"time"
)
type CustomTime struct {
time.Time
}
const ctLayout = "2006-01-02"
func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), "\"")
if s == "null" {
ct.Time = time.Time{}
return
}
ct.Time, err = time.Parse(ctLayout, s)
return
}
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ctLayout))), nil
}
var nilTime = (time.Time{}).UnixNano()
func (ct *CustomTime) IsSet() bool {
return ct.UnixNano() != nilTime
}
func (c CustomTime) Value() (driver.Value, error) {
return driver.Value(c.Time), nil
}
func (c *CustomTime) Scan(src interface{}) error {
switch t := src.(type) {
case time.Time:
c.Time = t
return nil
default:
return fmt.Errorf("column type not supported")
}
}
你的问题是,现在我需要将另一个字段格式化为精确到毫秒的时间,例如ctLayout = "2006-01-02 15:04:05.000"
。我的问题是,如何设置这个,以便我不需要复制整个结构,是否有一种方法可以在Entry结构中传递ctLayout字符串,并在实用函数中使用它。我相信有办法,但我无法弄清楚如何做。
如果我需要创建一个CustomTimeMS类,那就这样吧,但这似乎是很多重复的代码。
谢谢。
英文:
I have a utility function for marshalling and unmarshalling custom time formats in go that is working fine for one format string and is used in a JSON model as follows
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
Duid string `json:"duid"`
IntervalCount int32 `json:"intervalCount"`
Intervals []int32 `json:"intervals"`
RequestId string `json:"requestId"`
RequestTimestamp time.Time `json:"requestTimestamp"`
TradingDate utils.CustomTime `json:"tradingDate"`
Unit string `json:"unit"`
RebidExplanation string `json:"rebidexplanation"`
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
}
The utility function being.
package utils
import (
"database/sql/driver"
"fmt"
"strings"
"time"
)
type CustomTime struct {
time.Time,
}
const ctLayout = "2006-01-02"
func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), "\"")
if s == "null" {
ct.Time = time.Time{}
return
}
ct.Time, err = time.Parse(ctLayout, s)
return
}
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ctLayout))), nil
}
var nilTime = (time.Time{}).UnixNano()
func (ct *CustomTime) IsSet() bool {
return ct.UnixNano() != nilTime
}
func (c CustomTime) Value() (driver.Value, error) {
return driver.Value(c.Time), nil
}
func (c *CustomTime) Scan(src interface{}) error {
switch t := src.(type) {
case time.Time:
c.Time = t
return nil
default:
return fmt.Errorf("column type not supported")
}
}
My issues, is that I now need to format one of the other fields with exactly 3ms fields such as ctLayout = "2006-01-02 15:04:05.000".
My question is, how do I set this up so I don't need to duplicate the entire structure, Is there a way I can pass in the ctLayout string in the Entry structure and pick it up in the utility function. I'm sure there is, but I can't for the life of me work out how.
If I need to create a CustomTimeMS class, then so be it, but it seems like a lot of duplicate code.
Thanks.
答案1
得分: 2
两种方法:
方法一
在结构体Entry中添加一个导出字段CTLayout
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
...
...
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
CTLayout string `json:"-"`
}
更新MarshalJSON函数
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ct.CTLayout))), nil
}
由于CTLayout是导出的,它可以从任何地方访问。
在调用json.Marshal
之前,更新CTLayout为所需的值。
====================================
方法二
添加一个未导出的字段
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
...
...
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
ctLayout string
}
更新MarshalJSON函数
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ct.ctLayout))), nil
}
创建一个导出的辅助函数
func (ct *CustomTime) Marshal(ctLayout string) ([]byte, error) {
// 在这里可以进行其他操作
// 我们将执行明显的操作
ct.ctLayout = ctLayout
return ct.MarshalJSON()
}
调用ct.Marshal(你的布局)
。
如果你可以访问包含CustomTime的包,可以直接修改CustomTime实例,并将Marshal函数设为未导出。
英文:
Two ways:
One
Add an exported field CTLayout to struct Entry
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
...
...
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
CTLayout string `json:"-"`
}
Update the MarshalJSON function
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ct.CTLayout))), nil
}
Since CTLayout is exported it's accessible from anywhere.
Update CTLayout with what you need before calling json.Marshal
on the CustomTime value
====================================
Two
Add an unexported field
type Entry struct {
ID int `json:"id"`
AuthorisedBy string `json:"authorisedBy"`
...
...
AcceptedTimestamp time.Time `json:"acceptedTimestamp"`
ctLayout string
}
update the MarshalJSON function
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
if ct.Time.UnixNano() == nilTime {
return []byte("null"), nil
}
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ct.ctLayout))), nil
}
create a exported helper marshal function
func (ct *CustomTime) Marshal(ctLayout string) ([]byte, error) {
// You can do whatever else you want here
// we'll do the obvious
ct.ctLayout = ctLayout
return ct.MarshalJSON()
}
call ct.Marshal(your layout)
If you have access to the package in which CustomTime exists go ahead and modify the CustomTime instance directly and make Marshal unexported if you want.
答案2
得分: 1
在Entry
和CustomTime
的MarshalJSON
方法之间,没有一种自动的方式可以传递信息,即使使用结构标签也不行。我相信,除了创建一个新类型之外,你唯一能做的事情就是在CustomTime
中添加一个新字段,然后在序列化Entry
实例之前,手动将其设置为某个值,以指示MarshalJSON
应该如何格式化输出。我的建议是创建一个新类型,虽然有点重复,但更清晰。
请注意,嵌入允许你避免重新实现那些在嵌入类型和被嵌入类型中应该具有相同行为的方法。所以,在你的情况下,你只需要重新实现JSON序列化和反序列化方法。isset、value和scan方法不需要重新实现。
英文:
There isn't an automatic way to pass information from Entry
to CustomTime
's MarshalJSON
method, not even using the struct tags. The only thing you can do, I believe, apart from creating a new type, is to add a new field to CustomTime
and then, just before marshaling the Entry
instance, manually set it to some value that will indicate how MarshalJSON
should format the output. My suggestion would be to create a new type, a bit repetitive but cleaner.
Note that embedding allows you to avoid having to re-implement those methods that are supposed to behave the same in both, the embedded type and the embedding type. So, in your case, you only need to re-implement the json marshaling and unmarshaling methods. The isset, value, and scan methods don't have to be re-implemented.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论