loading...

Use Anonymous Structs For JSON Marshalling in Go

wagslane profile image Lane Wagner Originally published at qvault.io on ・2 min read

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 {
        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:

  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 deallocation 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 {
        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.

Follow me on Dev.to: wagslane

The post Use Anonymous Structs For JSON Marshalling in Go appeared first on Qvault.

Discussion

pic
Editor guide
Collapse
tmack8080 profile image
tmack8080

Not a programmer.

What about nested structs? Can anonymous structs be used?

For example, I want to mock a JSON response from this API: zabbix.com/documentation/4.2/manua...

Response:

{
    "jsonrpc": "2.0",
    "result": [
        {
            "itemid": "23296",
            "clock": "1351090996",
            "value": "0.0850",
            "ns": "563157632"
        },
        {
            "itemid": "23296",
            "clock": "1351090936",
            "value": "0.1600",
            "ns": "549216402"
        },
    ],
    "id": 1
}

Thanks

Collapse
torresjrjr profile image
Byron Torres

Thank you for this! I was struggling with making my own TCP JSON unmarshaller, and this never occurred to me. I'll try this out.