英文:
How do I inline fields when marshalling JSON?
问题
我有一个名为MonthYear
的类型定义,如下所示:
type MonthYear time.Time
func (my *MonthYear) MarshalJSON() ([]byte, error) {
t := time.Time(*my)
return json.Marshal(&struct {
Month int `json:"month"`
Year int `json:"year"`
}{
Month: int(t.Month()) - 1,
Year: t.Year(),
})
}
我在许多不同的结构体中包含它,例如:
type Event struct {
Name string `json:"name"`
Date MonthYear
}
type Item struct {
Category string `json:"category"`
Date MonthYear
}
如何内联MonthYear
类型,以便生成的JSON不包含任何嵌入对象?
我希望结果看起来像{ "name": "party", "month": 2, "year": 2017 }
和{ "category": "art", "month": 3, "year": 2016 }
,而无需为每个结构体编写MarshalJSON
方法。
英文:
I have a type MonthYear
defined like
type MonthYear time.Time
func (my *MonthYear) MarshalJSON() ([]byte, error) {
t := time.Time(*my)
return json.Marshal(&struct {
Month int `json:"month"`
Year int `json:"year"`
}{
Month: int(t.Month()) - 1,
Year: t.Year(),
})
}
I'm including it a lot of different structs like
type Event struct {
Name string `json:"name"`
Date MonthYear
}
type Item struct {
Category string `json:"category"`
Date MonthYear
}
How do I inline the MonthYear
type so that the resulting JSON does not have any embedded objects?
I want the result to look like { "name": "party", "month": 2, "year": 2017 }
and { "category": "art", "month": 3, "year": 2016 }
without having to write the MarshalJSON for each of the structs.
答案1
得分: 5
我知道这不是你希望得到的答案,但在encoding/json
包添加内联支持之前,你可以使用以下解决方法:
将你的MonthYear
定义为一个结构体,例如:
type MonthYear struct {
t time.Time
Month int `json:"month"`
Year int `json:"year"`
}
为了方便创建,可以添加一个可选的构造函数:
func NewMonthYear(t time.Time) MonthYear {
return MonthYear{
t: t,
Month: int(t.Month()) - 1,
Year: t.Year(),
}
}
然后使用嵌入(匿名字段)而不是常规(命名)字段,以在JSON表示中实现“扁平化”/内联化:
type Event struct {
Name string `json:"name"`
MonthYear
}
type Item struct {
Category string `json:"category"`
MonthYear
}
作为额外的好处,你可以直接引用字段,例如Event.Year
或Event.Month
。
进行测试:
evt := Event{Name: "party", MonthYear: NewMonthYear(time.Now())}
fmt.Println(json.NewEncoder(os.Stdout).Encode(evt), evt.Year)
itm := Item{Category: "Tool", MonthYear: NewMonthYear(time.Now())}
fmt.Println(json.NewEncoder(os.Stdout).Encode(itm))
输出结果(在Go Playground上尝试):
{"name":"party","month":10,"year":2009}
<nil> 2009
{"category":"Tool","month":10,"year":2009}
<nil>
注意:这里的MonthYear.t
时间字段没有起作用(也不会被编组)。如果不需要原始的time.Time
,可以将其删除。
英文:
I know this is not the answer you wish to receive, but until inline support is added to the encoding/json
package, you may use the following workaround:
Make your MonthYear
a struct, e.g.:
type MonthYear struct {
t time.Time
Month int `json:"month"`
Year int `json:"year"`
}
An optional, constructor function for easy creation:
func NewMonthYear(t time.Time) MonthYear {
return MonthYear{
t: t,
Month: int(t.Month()) - 1,
Year: t.Year(),
}
}
And use embedding (anonymous field) instead of a regular (named) field to get "flattened" / inlined in the JSON representation:
type Event struct {
Name string `json:"name"`
MonthYear
}
type Item struct {
Category string `json:"category"`
MonthYear
}
As an extra, you'll be able to refer to fields directly like Event.Year
or Event.Month
which is nice.
Testing it:
evt := Event{Name: "party", MonthYear: NewMonthYear(time.Now())}
fmt.Println(json.NewEncoder(os.Stdout).Encode(evt), evt.Year)
itm := Item{Category: "Tool", MonthYear: NewMonthYear(time.Now())}
fmt.Println(json.NewEncoder(os.Stdout).Encode(itm))
Output (try it on the Go Playground):
{"name":"party","month":10,"year":2009}
<nil> 2009
{"category":"Tool","month":10,"year":2009}
<nil>
Note: The MonthYear.t
time field does not play a role here (it is not marshaled either). You can remove it if the original time.Time
is not needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论