Demystifying Go’s JSON Marshaling With Embedded Structs

Kacper Bąk
3 min readMay 17, 2023

Go, a statically typed, compiled programming language designed at Google, has become a popular choice for various applications due to its simplicity and efficiency. One area where Go really shines is in handling JSON data.

Today, we will explore an interesting aspect of Go’s JSON marshaling, specifically when dealing with embedded structs. We will use a simple Event a struct that has an Id of type int and an embedded time.Time struct.

Here’s the code snippet:

package main

import (
"encoding/json"
"fmt"
"time"
)

func main() {
type Event struct {
Id int
time.Time
}

event := Event{
Id: 1234,
Time: time.Now(),
}

b, _ := json.Marshal(event)
fmt.Print(string(b))
}

This program defines an Event type with an Id field and a time.Time field creates an instance of Event, and then attempts to marshal it into a JSON object.

If we run this program, we might be surprised by the output:

"2009-11-10T23:00:00Z"

The Id field is missing! Why is that?

Understanding Embedded Structs in Go

In Go, when we include a type within another type without giving it a name, it’s called an embedded struct. The methods, and fields of the embedded struct become part of the outer struct. However, when it comes to marshaling to JSON, Go’s encoding/json package treats embedded fields differently.

In our case, time.Time is an embedded struct. It comes with its own MarshalJSON method which formats the time as a string in RFC3339 format. So when we marshal an Event, encoding/json uses this MarshalJSON method for the Time field, but ignores the Id field because it doesn't have an explicit name in the struct.

Solution

To fix this, we need to provide an explicit name for the time.Time field in our struct:

type Event struct {
Id int `json:"Id"`
Time time.Time `json:"Time"`
}

Now if we run the program again, we get the expected output:

{"Id":1234,"Time":"2009-11-10T23:00:00Z"}

In conclusion, Go’s JSON marshaling with embedded structs might seem confusing at first, but once we understand how Go treats embedded structs, it becomes easy to predict its behavior. By providing explicit field names, we can control how our structs are marshaled to JSON, and ensure that all the necessary data is included.

And that’s it! I hope you found this article helpful. Stay tuned for more insights into the world of Go programming.

--

--