Custom Unmarshalers in Go. Easier than you’d think.
When first presented with the notion of writing a custom unmarshaler (Go’s moniker for a deserializer) in Go, I panic
ked 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.