DEV Community

loading...

How to Handle Missing Fields From A Struct in Go?

#go
Mohammad Aziz
I write bugs 🐛
Originally published at iaziz786.com ・2 min read

Suppose you have a variable of the type of the struct mentioned below which you wanted to Unmarshal.

type Building struct {
    WindowCount int `json:"window_count"`
    Doors       int `json:"doors"`
}
Enter fullscreen mode Exit fullscreen mode

In case the data is coming from an unreliable source, you can never be sure that the value you will receive will always have the correct format. You have to be cautious when working around data like this. If you are new in Go you might be wondering what will happen you Unmarshal and incorrect value?

Compiler Loves Zero Values

If you unmarshal the code into a variable you will get the zero value for the fields WindowCount, and Doors. Which in some case can lead to bug because you might have different logic for missing case.

var b Building
json.Unmarshal([]byte(""), &b)
fmt.Printf("%+v\n", b)
// Output:
// {WindowCount:0 Doors:0}
Enter fullscreen mode Exit fullscreen mode

Pointer To Rescue 🦸

If you have worked with pointers in languages like C, C++ you might have some opinion. You either like it or you hate it. Most people fall into the latter group. Working with pointers in Golang is fun because things are declarative comparatively.

If you set the field type to a pointer of that type it will be nil in case the field is missing

type Building struct {
    WindowCount *int `json:"window_count"`
    Doors       *int `json:"doors"`
}
Enter fullscreen mode Exit fullscreen mode

Unmarshalling it has the following effect:

var b Building
json.Unmarshal([]byte(`{"window_count": 2}`), &b)
fmt.Printf("%+v\n", b)
// Output:
// {WindowCount:0xc000016170 Doors:<nil>}
Enter fullscreen mode Exit fullscreen mode

The WindowCount holds the pointer to an integer which holds the value 2. Whereas since doors is missing from the JSON string it will set to nil thanks to the zero value of pointers. You can now write your logic in case of the absence of that value.

Conclusion

Since the field of a struct is part of the contract of the struct it can't go missing entirely instead you need to handle it little differently. The most preferable way is to convert the type to a pointer of the same type and when in some case the value is missing it will be set to the zero value, which is nil.

Discussion (1)

Collapse
rafaacioly profile image
Rafael Acioly

Nice trick Mohammad I didn't know that if we use pointers on types we get nil values, but be careful with this because you can have panic errors if you forgot to check if it's a nil or int, what I would do is keep the type as it is and add a method to check if the field is not zero, like this:

type Building struct {
    WindowCount int `json:"window_count"`
    Doors       int `json:"doors"`
}

func (b Building) HasDoors() bool {
    return b.Doors > 0
}
Enter fullscreen mode Exit fullscreen mode

This way I don't need to check if the field is nil and can still perform arithmetic operations.

var b = Building{WindowCount: 1}
b.Doors += 1
Enter fullscreen mode Exit fullscreen mode