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?
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:
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:Company its own MarshalJSON method that marshalls all of the fields including the timestamp. You could do this byCompany but without the embedding, and marshalling that.fmt.Sprintf({"key1":%s,"key2":%s,...}, m1, m2, ...) yourself.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
}
https://play.golang.org/p/PhZXPauSyz
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With