Unmarshalling JSON structs containing interfaces in Go

As far as developing goes, Go is the most fun programming language I’ve used until now. If you are not familiar with Go, you should really check it out. However, all languages have their own barriers and obstacles. This blog post describes the most recent one I’ve had to tackle: Unmarshalling JSON structs that contain interfaces as their attributes.

What’s the problem you may ask? Well, if you have an interface (we won’t be talking about empty interfaces this time) encoding/json doesn’t handle decoding those for you. You need to be very specific on which types you pass to the decoder. But we want to be able to do so, so how do we manage to do that?

Lets build a simple TV guide. The guide consists of some technical information about the guide itself like company’s name, legal notices, ID and so forth. The guide also contains the library itself, which is conceptually everything you can hit play on: movies, series, music, radio etc. We’ll start with defining what is actually a Playable is:

That will be our interface for playing various types of items inside the library. Like we said, we want the library to contain everything we can hit the play button on. Next we will need a TVGuide struct to hold all of our data inside:

As you can see, we already lay the groundwork for JSON decoding. Also, we used type aliasing and define a slice of Playable as Playables. We do that because, turns out that functions declared in Go don’t go well with interface types as receivers. Now we need something to implement that interface. Hence, the Movie struct:

We create the Movie struct and instantly implement the Playable interface. Here too, we define our struct attributes as JSON decoding compatible.

Just one more thing and we are done. We need to implement UnmarshalJSON for Playables in order to manually decide, depending on the type which JSON field to parse, into what implementation of a Playable should we decode to. The following code does exactly that:

We start with unmarshalling the passed data into a map of strings as keys and pointers to json.RawMessage as value.  This way we ensure we can iterate over all the fields received under the library JSON field and check for what Playable implementation they should decode to. Because we support only Movie implementations for now, we will parse only movies.

We iterate through all the library fields and enter into the movies JSON. Because we want to decode multiple movies, that value is a list of one or more JSONs. Every one of those matches exactly to what we defined in Movies struct. This describes exactly the JSON we require:

The way we receive those types of JSON string is entirely up to you, Whether it is by a web-service, directly from files, receiving from a raw socket and so forth. We will skip that part because it is entirely out of scope for this post. But there is a common way to handle all of those. This pseudo code completes this blog post by unmarshalling the above JSON string and printing it for us to be joyful it all works.

Running this will result in the required output:

{0rkaTV NO RUNNING WITH SCISSORS 0x1337H4X0R [0xc0420505e0 0xc042050640]}

The complete code is available on gist and also on Go’s playground.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s