Go JSON
Marshaling from and Unmarshaling between JSON and structs in Go, is done using the built-in encoding/json
package and often relies heavily on struct tags.
Struct tags can be used to define how a field name maps to a name in our JSON output. This is especially useful because the encoding/json
package is only able to see exported fields, so we are forced to capitalize the field names that we want to encode.
The json
struct tag syntax is json:"name,option"
. To ignore a field completely, use "-"
, as seen in the Password
field. As the name implies, omitempty
leaves out the field in the JSON output when left empty.
Go can marshal strings, integers, floats and time fields, but will also encode slices, arrays, maps, and nested structs correctly.
When you need to distinguish between zero values or missing values, you use a pointer value instead, as seen in the DeletedAt
field.
type User struct {
Username string `json:"username"`
Password string `json:"-"`
}
type Page struct {
Title string `json:"title"`
NrOfViews int `json:"nr_of_views"`
Tags []string `json:"tags"`
User User `json:"user"`
CustomData map[string]string `json:"custom_data"`
CreatedAt time.Time `json:"created_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
Encoding this structure is fairly straightforward using the json.Marshal()
method.
page := Page{
Title: "My page",
NrOfViews: 12,
Tags: []string{"go", "json"},
User: User{
Username: "gumuz",
Password: "secret123",
},
CustomData: map[string]string{"os": "osx", "browser": "firefox"},
CreatedAt: time.Now(),
}
data, err := json.Marshal(page)
if err != nil {
panic(err)
}
The resulting JSON will look like this, but without the indentation.
{
"title": "My page",
"nr_of_views": 12,
"tags": [
"go",
"json"
],
"user": {
"username": "gumuz"
},
"custom_data": {
"browser": "firefox",
"os": "osx"
},
"created_at": "2020-08-17T21:02:35.242834+02:00"
}
We can see that the Password
field was left out, as is the DeletedAt
field since it was marked as omitempty
.
We can decode the data by initializing an empty struct and using json.Unmarshal()
to populate it.
var anotherPage Page
err = json.Unmarshal(data, &anotherPage)
if err != nil {
panic(err)
}
When we compare the two structs, we can see they’re nearly identical. The Password
field was intentionally left out when we marshaled the data to JSON, and we lost some precision on the CreatedAt
field.
fmt.Println(fmt.Sprintf("%+v\n", page))
fmt.Println(fmt.Sprintf("%+v\n", anotherPage))
// {Title:My page NrOfViews:12 Tags:[go json] User:{Username:gumuz Password:secret123} CustomData:map[browser:firefox os:osx] CreatedAt:2020-08-17T21:02:35.242834 +0200 CEST m=+0.000288531 DeletedAt:<nil>}
// {Title:My page NrOfViews:12 Tags:[go json] User:{Username:gumuz Password:} CustomData:map[browser:firefox os:osx] CreatedAt:2020-08-17T21:02:35.242834 +0200 CEST DeletedAt:<nil>}