Improved Golang JSON unmarshaling with time and URL
Often times we need to unmarshal upstream data with unique constraints. Such as custom data types, or custom parsing of specific formats. Using the standard library could be impractical, and handling long structs manually can be tedious; however there is an alternative.
While it is possible to use the
json.UnmarshalJSON interface to create a custom
unmarshaler using a temporary struct that mirrors our data type this
can become complicated when the data type has many fields, dozens or hundreds.
It becomes laborious to create a temp value with hundreds of fields. It’s annoying
to create and clutters the source code; however there another option.
We can use an embedded clone type of our data and only handle the fields that require our attention.
For our example we will use this Person struct. We want to manually handle the time and URL. These fields could be any that the standard Json library can not handle itself, or that require manual handling; such as parsing a custom time format from a legacy system.
The first thing we are going to do in our custom Unmarshaling function is declare a new type that is identical in layout as our Person data type.
After we declare our type, we will create a temporary struct with the fields we have to handle manually, and our type embedded as a pointer.
We set our method receiver, and convert it to our clone type. We can do this because they are identical in layout and underlying type. We must ensure that we convert the method receiver as a pointer type.
Now we will unmarshal as normal into our temporary variable. Because our embedded type is a pointer all unmarshaled values, except those we are handling specifically, are assigned to the fields of our method receiver; that is to our original data structure.
Next we handling our custom fields manually in any way that is required. If we needed to validate a UUID, or some esoteric serialized format, we would do it here. We assign the final value to the correct field of the method receiver.
And lastly we return without error.
This little bit of code requires some smoke and mirrors to get working, but once you have used it you will find yourself using even for small types that require custom handling.
Here is the full example with all of the previous code blocks together. You can also view a working example on the Go Playground.