英文:
Issue with using custom JSON Marshal() for Embedded Structs
问题
我正在尝试定义一个自定义的JSON编组器,以特定的格式显示一些时间信息。理想情况下,我希望有一个结构体来存储创建/修改的值,然后将它们嵌入到需要跟踪该信息的结构体中。此外,我想在JSON编组器中定义一个自定义日期格式,供客户端应用程序使用。
我目前有两个结构体:
type Timestamp struct {
Created time.Time
Modified time.Time
}
type Company struct {
Id string
Name string
Timestamp
}
我想将Timestamp结构体嵌入到需要记录项目更新/创建时间的对象中。没有什么特别的问题。
问题出在我定义的这段代码上:
func (t Timestamp) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
CreatedFormatted string
}{
CreatedFormatted: t.Created.Format("Monday Jan _2 15:04:05 2006"),
})
}
当我尝试编组Company
时,我只看到了Timestamp的JSON,而没有看到Company的JSON。我原本以为Company结构体和Timestamp结构体的内容都会被显示出来。我在这里做错了什么吗?
英文:
I am attempting to define a custom JSON marshaler to display some time information in specific formats. Ideally, I'd like to have a struct that stores created/modified values and then embed them into structs that need to keep track of that information. In addition, I want to define a custom date format in the JSON marshaler to use from a client-side application.
I currently have two structs
type Timestamp struct {
Created time.Time
Modified time.Time
}
type Company struct {
Id string
Name string
Timestamp
}
I want to embed the Timestamp struct into objects that will need to record when items are updated/created. Nothing insane there.
My issues comes when I define
func (t Timestamp) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
CreatedFormatted string
}{
CreatedFormatted: t.Created.Format("Monday Jan _2 15:04:05 2006"),
})
}
When I go to marshal Company
, I only see the json for the Timestamp but nothing for the Company. I would have thought that the contents of the Company struct and the Timestamp struct would have been displayed. Am I doing something wrong here?
答案1
得分: 5
当一个嵌入在结构体中的类型提供了一个方法时,该方法成为了嵌入结构体的一部分。由于Company
嵌入了Timestamp
,所以Timestamp
的MarshalJSON
方法也对Company
可用。当JSON尝试对Company
进行编组时,它发现它有一个MarshalJSON
方法并调用它,而该方法只编组了时间戳字段。结构体的默认行为是将每个字段编组为JSON对象中的一个键,但这一行为被覆盖了。
你可以这样做:
- 不使用结构体嵌入,只将
Timestamp
作为一个普通字段(即使声明为Timestamp Timestamp
)。这样,Company
就不会继承其方法,JSON将按预期工作(但是其他依赖嵌入的代码可能会改变)。或者: - 为
Company
提供自己的MarshalJSON
方法,该方法编组包括时间戳在内的所有字段。你可以通过以下方式实现:
a. 将字段复制到一个与Company
完全相同但没有嵌入的不同类型中,并对其进行编组。
b. 将字段复制到一个映射中,并对其进行编组。
c. 对每个字段进行独立的编组,然后自己拼接字符串,例如fmt.Sprintf(
{ "key1":%s, "key2":%s, ...}, m1, m2, ...)
。
英文:
When a type embedded in a struct provides a method, that method becomes a part of the embedding struct. Since Company
embeds Timestamp
, Timestamp
's MarshalJSON
is available for Company
as well. When json is looking to marshal a Company
it sees that it has a MarshalJSON
method and calls it — and the method it finds only marshals the timestamp field. The default behavior of structs (to marshal each field into its own key in a JSON object) is overridden.
What you can do:
- Don't use struct embedding, just make
Timestamp
a regular field (even if you declare it asTimestamp Timestamp
). ThenCompany
won't inherit its methods and JSON will work as expected (but other parts of your code that expect embedding might change). Or: - Give
Company
its ownMarshalJSON
method that marshalls all of the fields including the timestamp. You could do this by
a. Copying the fields into a different type that is identical toCompany
but without the embedding, and marshalling that.
b. Copying the fields into a map and marshalling that.
c. Marshalling each of the fields independently and string-pasting <code>fmt.Sprintf({"key1":%s,"key2":%s,...}
, m1, m2, ...)</code> yourself.
答案2
得分: 2
通过在Company
中嵌入Timestamp
,你不仅共享了成员变量,还共享了方法。这意味着你提供了Company.MarshalJSON
方法,该方法由json
包用于对整个结构进行编组。为了查看所有字段,你需要为Company
结构实现一个显式的编组器。
如果你只想以特定的方式格式化时间戳,另一种解决方案是提供自己的time.Time
并在那里提供JSON编组器。
例如:
type AccessTime time.Time
func (t AccessTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t).Format("Monday Jan _2 15:04:05 2006"))
}
type Timestamp struct {
Created AccessTime
Modified AccessTime
}
https://play.golang.org/p/PhZXPauSyz
英文:
By embedding Timestamp
in Company
you not only shared member variables, but also the methods. This means you provided Company.MarshalJSON
method, which is then used by json
package for marshalling the whole structure. In order to see all the fields you would need to implement an explicit marshaler for Company
structure as well.
If you only want to format the timestamp in a specific way, the other solution would be to provide your own time.Time
and providing JSON marshaller there.
For example:
type AccessTime time.Time
func (t AccessTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t).Format("Monday Jan _2 15:04:05 2006"))
}
type Timestamp struct {
Created AccessTime
Modified AccessTime
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论