photo 1493476523860 a6de6ce1b0c3

Use Anonymous Structs For JSON Marshalling in Go

Go is a language built for the web. The Go standard library comes with everything we need to stand up a production web server. Today we are going to explore marshaling JSON using anonymous structs. Anonymous structs can help keep API handlers clean and simple.

What Is A Struct?

Go’s structs are typed collections of fields. They’re useful for grouping data together to form records.
https://gobyexample.com/structs

Structs don’t have behavior, just data. For web developers, it can be helpful (though an oversimplification) to think of most structs as just JSON data.

We can declare a struct:

type car struct {
	make    string
	model   string
	mileage int
}

And we can create and instance of it:

newCar := car{
	make:    "Ford",
	model:   "taurus",
	mileage: 200000,
}

What Makes a Struct Anonymous?

An anonymous struct is declared in the same statement that initializes an instance of it:

newCar := struct {
	make    string
	model   string
	mileage int
}{
	make:    "Ford",
	model:   "Taurus",
	mileage: 200000,
}

Anonymous structs are great for unmarshalling JSON data in HTTP handlers. If a struct is only meant to be used once, then it makes sense to declare it in such a way that developers down the road won’t be tempted to use it again:

func createCarHandler(w http.ResponseWriter, req *http.Request) {
	defer req.Body.Close()
	decoder := json.NewDecoder(req.Body)
	newCar := struct {
		Make    string `json:"make"`
		Model   string `json:"model"`
		Mileage int    `json:"mileage"`
	}{}
	err := decoder.Decode(&newCar)
	if err != nil {
		log.Println(err)
		return
	}
	makeCar(newCar.Make, newCar.Model, newCar.Mileage)
	return
}

Don’t Use Map[string]interface{}

Instead of declaring a quick anonymous struct for JSON unmarshalling, I’ve often seen map[string]interface{} used. This is bad in most scenarios because:

  1. No type checking – if the client sends “model” as a boolean, which was supposed to be a string, then unmarshalling into a map won’t catch the error
  2. Vague – After unmarshalling the data, we must use runtime checks for existence. If those checks aren’t thorough, it can lead to nil pointer dereference panic exceptions being thrown.
  3. Verbose – Digging into the struct isn’t as simple as accessing a named field as in “newCar.model”. Instead, its something like:
func createCarHandler(w http.ResponseWriter, req *http.Request) {
	myMap := map[string]interface{}{}
	decoder := json.NewDecoder(req.Body)
	err := decoder.Decode(&myMap)
	if err != nil {
		log.Println(err)
		return
	}
	model, ok := myMap["model"]
	if !ok {
		fmt.Println("field doesn't exist")
		return
	}
	modelString, ok := model.(string)
	if !ok {
		fmt.Println("model is not a string")
	}
	// do something with model field
}

Anonymous structs can clean up your API handlers if used properly. The strong typing they offer while still being a “one-off” solution is a powerful tool.

More Clean Code Articles:
Don’t Go To Casting Hell; Use Default Native Types in Go
Range Over Ticker In Go With Immediate First Tick

More Golang Articles:
Wrapping Errors in Go – How to Handle Nested Errors
How To Build JWT’s in Go (Golang)

Thanks For Reading!

Follow us on Twitter @q_vault if you have any questions or comments

Take game-like coding courses on Qvault Classroom

Subscribe to our Newsletter for more educational articles

%d bloggers like this: