使用自定义的JSON Marshal()函数处理嵌入结构的问题

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

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,所以TimestampMarshalJSON方法也对Company可用。当JSON尝试对Company进行编组时,它发现它有一个MarshalJSON方法并调用它,而该方法只编组了时间戳字段。结构体的默认行为是将每个字段编组为JSON对象中的一个键,但这一行为被覆盖了。

你可以这样做:

  1. 不使用结构体嵌入,只将Timestamp作为一个普通字段(即使声明为Timestamp Timestamp)。这样,Company就不会继承其方法,JSON将按预期工作(但是其他依赖嵌入的代码可能会改变)。或者:
  2. 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:

  1. Don't use struct embedding, just make Timestamp a regular field (even if you declare it as Timestamp Timestamp). Then Company won't inherit its methods and JSON will work as expected (but other parts of your code that expect embedding might change). Or:
  2. Give Company its own MarshalJSON 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 to Company 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({&quot;key1&quot;:%s,&quot;key2&quot;:%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(&quot;Monday Jan _2 15:04:05 2006&quot;))
}

type Timestamp struct {
	Created  AccessTime
	Modified AccessTime
}

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

huangapple
  • 本文由 发表于 2015年11月14日 06:13:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/33702630.html
匿名

发表评论

匿名网友

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

确定