Json serialization of optional fields in Go
A quick guide to omitempty, default values and nil
Go is widely being used for backend programming and its community is growing larger each day. Personally I love coding in Go.
Lately I was working on a project that has optional fields in its datasource and needed to manipulate the response based on business needs.
I thought it’d be a good idea to make a small tutorial about it.
Let’s create a simple struct and see the response:
Don’t hate me for using uint64 for childrenCount, it’s just an example. :)
We are deserializing the entity from our datasource before serializing it.
The corresponding response is:
Let’s put an alias for it to use in json serializing/deserializing.
And the response will be:
To serialize nonexistent values with its defaults
The default approach sets default values for optional/nonexistent fields.
Do you see what could cause a problem here?
In my datasource I have an optional field called childrenCount, which holds the number of kids each employee have. For one employee it’s 0 and for another employee it’s null. I wouldn’t want to show 0 for both fields,
because having 0 and not having information is two different things.
If you can’t ever have 0 in your datasource, you can safely behave 0 as if it’s null. It’s great! You can stop here.
But now for this case, I need to differentiate between 0 and null.
To serialize both nonexistent and “empty” values
This approach omits both null and empty values. (such as 0, empty string).
Go has an omitempty tag that helps us to not serialize empty fields.
Let’s add ownedCars field to our entity and implement it.
Response didn’t change because ownedCars is not empty.
So let’s say it’s null for ownedCars.
And the response is now:
It didn’t serialize the empty value, yay!
But remember childrenCount was also optional, so let’s put omitempty to it and let’s say this time we have Julia with us and she has no kids.
Now both childrenCount and ownedCars is omitted in response, since Go reads 0 as an empty value.
If it’s okay for your business requirement, it’s great! You can stop here.
But now for this case, I lost the childrenCount value, even though I know it’s 0 in the datasource as a valid value for this case.
To not serialize nonexistent fields, but serialize “empty” fields
This approach omits null values and leaves zeros and empty strings as it is.
So this was our latest struct:
(Let’s say goodbye to Jason and continue with Julia.)
The main reason for Go to assign default values to these fields is because types such as uint64 or string can not be nil (for instance, slices can be nil), but their pointers can. So we must hold a pointer reference in our entity.
Such as:
And now the response will be:
Now it works as expected!
If you’d like to convert this entity to another struct, such as EmployeeDto,
you can again hold a pointer reference and set the fields directly, it’ll work.
If it’s okay for your business requirement, it’s great! You can stop here.
(For me, this was exactly what I wanted.)
But as you may have realized, this approach doesn’t serialize null fields.
If you need to explicitly serialize null fields as null in response, let’s continue.
To serialize nonexistent fields as null
This approach serializes null values as null and leaves “empty” fields as it is.
To this day, I haven’t yet needed to explicitly serialize null fields, just omitting them from response was enough for the client part to see it as null.
But if you need to use it that way, there is a library called guregu/null that is widely used by Go community.
I personally didn’t use it in production, but it should be fine.
Its usage is very easy:
And now the response will be:
Ta-da!
I wanted to show a quick guide on how to manipulate json’s serialization method according to our needs, as I struggled to find a guide that shows different behaviors step-by-step.
All feedbacks are welcome and I hope it was helpful. :)
Thank you for reading! ❤️