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/structsStructs 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 {
fmt.Println(err.Error())
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:
- 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
- Vague – After unmarshalling the data, we must use runtime checks for existence. If those checks aren’t thorough, it can lead to nil pointer deallocation exceptions being thrown.
- 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 {
fmt.Println(err.Error())
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.
Thanks For Reading
Hit me up on twitter @wagslane if you have any questions or comments.
The post Use Anonymous Structs For JSON Marshalling in Go appeared first on Boot.dev.