Encoding Structs
In this chapter we’re going to head back to the showMovieHandler
method that we made earlier and update it to return a JSON response which represents a single movie in our system. Similar to this:
Instead of encoding a map to create this JSON object (like we did in the previous chapter), this time we’re going to encode a custom Movie
struct.
So, first things first, we need to begin by defining a custom Movie
struct. We’ll do this inside a new internal/data
package, which later will grow to encapsulate all the custom data types for our project along with the logic for interacting with our database.
If you’re following along, go ahead and create a new internal/data
directory containing a movies.go
file:
And in this new file let’s define the custom Movie
struct, like so:
Now that’s done, let’s update our showMovieHandler
to initialize an instance of the Movie
struct containing some dummy data, and then send it as a JSON response using our writeJSON()
helper.
It’s quite simple in practice:
OK, let’s give this a try!
Restart the API and then visit localhost:4000/v1/movies/123
in your browser. You should see a JSON response which looks similar to this:
There are a few interesting things in this response to point out:
Our
Movie
struct has been encoded into a single JSON object, with the field names and values as the key/value pairs.By default, the keys in the JSON object are equal to the field names in the struct (
ID
,CreatedAt
,Title
and so on). We’ll talk about how to customize these shortly.If a struct field doesn’t have an explicit value set, then the JSON-encoding of the zero value for the field will appear in the output. We can see an example of this in the response above — we didn’t set a value for the
Year
field in our Go code, but it still appears in the JSON output with the value0
.
Changing keys in the JSON object
One of the nice things about encoding structs in Go is that you can customize the JSON by annotating the fields with struct tags.
Probably the most common use of struct tags is to change the key names that appear in the JSON object. This can be useful when your struct field names aren’t appropriate for public-facing responses, or you want to use an alternative casing style in your JSON output.
To illustrate how to do this, let’s annotate our Movies
struct with struct tags so that it uses snake_case
for the keys instead. Like so:
And if you restart the server and visit localhost:4000/v1/movies/123
again, you should now see a response with snake_case
keys similar to this:
Hiding struct fields in the JSON object
It’s also possible to control the visibility of individual struct fields in the JSON by using the omitempty
and -
struct tag directives.
The -
(hyphen) directive can be used when you never want a particular struct field to appear in the JSON output. This is useful for fields that contain internal system information that isn’t relevant to your users, or sensitive information that you don’t want to expose (like the hash of a password).
In contrast the omitempty
directive hides a field in the JSON output if and only if the struct field value is empty, where empty is defined as being:
- Equal to
false
,0
, or""
- An empty
array
,slice
ormap
- A
nil
pointer or anil
interface value
To demonstrate how to use these directives, let’s make a couple more changes to our Movie
struct. The CreatedAt
field isn’t relevant to our end users, so let’s always hide this in the output using the -
directive. And we’ll also use the omitempty
directive to hide the Year
, Runtime
and Genres
fields in the output if and only if they are empty.
Go ahead and update the struct tags like so:
Now when you restart the application and refresh your web browser, you should see a response which looks exactly like this:
We can see here that the CreatedAt
struct field no longer appears in the JSON at all, and the Year
field (which had the value 0
) doesn’t appear either thanks to the omitempty
directive. The other fields that we used omitempty
on (Runtime
and Genres
) are unaffected.
Additional Information
The string struct tag directive
A final, less-frequently-used, struct tag directive is string
. You can use this on individual struct fields to force the data to be represented as a string in the JSON output.
For example, if we wanted the value of our Runtime
field to be represented as a JSON string (instead of a number) we could use the string
directive like this:
And the resulting JSON output would look like this:
Note that the string
directive will only work on struct fields which have int*
, uint*
, float*
or bool
types. For any other type of struct field it will have no effect.