Custom Unmarshalers in Go. Easier than you’d think.

Daniel Hilton
2 min readJun 27, 2021

--

When first presented with the notion of writing a custom unmarshaler (Go’s moniker for a deserializer) in Go, I panicked a little. My experiences of this in C# were not fun ones, writing custom converters, messing around with Attributes and at worst… Convention Packs.

I was pleasantly surprised. All in all, my first custom unmarshaler took me an afternoon to write and was quite pleasurable.

Interfacing

Go’s interface structs are lovely little things. So long as you implement the receiver functions on it, any type struct will automatically be usable as that interface struct also.

package stufftype Greeter interface {
SayHello()
}
type JapaneseGreeter struct {}func (JapaneseGreeter) SayHello() {
fmt.Println("こんにちは")
}
type EnglishGreeter struct {}func (EnglishGreeter) SayHello() {
fmt.Println("Hello")
}

This trite example shows the simplicity of implementing interfaces in Go. Simply implement the SayHello function and your function automatically is recognised as an implementer of the Greeter interface.

Implement your Unmarshaler

For creating a custom Unmarshaler in Go, we do exactly this for the Umarshaler interface in the json package (https://golang.org/pkg/encoding/json/#Unmarshaler).

When your type implements this interface, Go’s json.Unmarshal func will _automatically_ detect if the type you are unmarshaling into implements Unmarshaler. If it does, it will use the logic you plumb in.

The signature is as follows: UnmarshalJSON([]byte) error

The json package takes care of the rest.

Another trite example

Given a type MyThing , say we want it to uppercase when unmarshaling the field Thing .

type MyThing struct {
Thing string
}
func (MyThing) UnmarshalJSON(b []byte) error {
var mt MyThing
if err := json.Unmarshal(b, &mt); err != nil {
return err
}

mt.Thing = strings.ToUpper(mt.Thing)
return nil
}

Given this JSON:

{
"thing": "uwotm8"
}

We would unmarshal it as such:

func DoTheDance(jsonBytes []byte) (MyThing, error) {
var u MyThing
err := json.Unmarshal(jsonBytes, &u)
if err != nil {
return nil, err
}

fmt.Println(u.Thing) //Prints UWOTM8
return u, nil
}

One can also implement unexported intermediary types, which will aid in custom unmarshaling. For more complex operations, you can implement an unexported type in your package for the initial call to json.Unmarshal inside your custom unmarshaler, giving you access to all your JSON fields easily. Then mangle them to your will for your final form.

I hope this was useful, I slung this article together whilst I was cooking my dinner, any feedback let me know, and don’t forget to give it a good clap too if you liked it.

--

--

Daniel Hilton
Daniel Hilton

Written by Daniel Hilton

Software Engineer for Compare The Market, dad, lover of tech, cars, software and all things Japanese.

Responses (5)