Go JSON: ALL You Need To Use encoding/json Effectively
Everything you need for using this useful package well, from simple encoding and decoding to more complex stuff…
In this write-up, we start with the easy parts of JSON… like turning data into JSON and getting data out of JSON. After that, we’ll look at more tricky things like how to use struct tags and how to change the way data gets turned into or out of JSON.
Throughout the article, we’ll supply practical examples and smart practices to ensure your code is both competent and productive.
Marshal/ Unmarshal JSON
If the terms Marshal and Unmarshal are already in your vocabulary, feel free to move on to the next section.
In essence, you use the Marshal
function to turn your struct into a series of JSON bytes, so to go in the other direction, you’ll want to use Unmarshal
.
Here’s a breakdown of how to use Marshal
with an example:
type Person struct {
Name string
Age int
}
func main() {
aiden := Person{"Aiden", 20}
bytes, err := json.Marshal(aiden)
if err != nil {
panic(err)
}
fmt.Println(string(bytes))
}
// {"Name":"Aiden","Age":30}
Want to make your JSON output look neat and pretty? Use this code piece:
bytes, _ := json.MarshalIndent(aiden, "", " ")
fmt.Println(string(bytes))
// result:
// {
// "Name": "Aiden",
// "Age": 30
// }
The output will be in a more readable format.
Remember, when using Unmarshal, it’s crucial to pass in a pointer as your second argument. Otherwise, an error like ‘json: Unmarshal(non-pointer main.Person)
’ will pop up.
func main() {
aidenJSON := `{"Name":"Aiden","Age":30}`
aiden := Person{}
err := json.Unmarshal([]byte(aidenJSON), &aiden)
if err != nil {
panic(err)
}
fmt.Println(aiden)
}
// {Aiden 30}
This is the baseline knowledge you need for small-scale projects, now, let’s get into what’s required for larger setups that need more performance.
“Why is the variable named aidenJSON instead of aidenJson for camel casing?”
In Go, the naming conventions opt for JSON
and HTML
instead of Json
or Html
, this is the style you’ll find in the standard Go library.
One last note, if a struct field is not marked as exported, it won’t be processed by Marshal or Unmarshal:
type Person struct {
name string
age int
}
Here, the fields ‘name
’ and ‘age
’ are not exported, so they won’t be included in the JSON conversion.
Supported struct tag
In my above example, I skipped over the use of struct tags.
So let’s clarify, struct tags help you modify how your fields appear in the JSON output, They can change the field names, omit certain fields, and more.
omitempty
: with this tag, fields are left out of the JSON output if they hold a default value. For example, empty strings, zeros, and false boolean values are excluded.
type Person struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
}
aiden := Person{Name: "Aiden", Age: 0}
// marshal result: {"name":"Aiden"}
-
: it tells the JSON processor to completely skip this field during both theMarshal
andUnmarshal
steps.
type Person struct {
Name string `json:"-"`
Age int `json:"age"`
}
aiden := Person{Name: "Aiden", Age: 30}
// marshal result: {"age":30}
“string”: turn numeric or boolean fields into string types when converting to and from JSON.
type Person struct {
Name string `json:"name,string"`
Age int `json:"age,string"`
Ban bool `json:"ban,string"`
}
aiden := Person{Name: "Aiden", Age: 30, Banned: false}
// marshal result: {"name":"\"Aiden\"","age":"30","ban":"false"}
Be cautious with this tag, as it will add extra double quotes to your output and this is also true when you’re unmarshaling the JSON back into a struct.
“inline”: This tag includes all fields of an embedded struct within the parent struct, making them appear as if they belong to the parent.
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Vehicle `json:",inline"` // <----
}
type Vehicle struct {
Model string
Version string
}
// unmarshal result: {"name":"Aiden","age":30,"Model":"Audi","Version":"A4"}
Custom Marshal and Unmarshal
You have the option to modify how marshalling and unmarshalling work to fit your particular use case. To do this, simply write your own implementations for the MarshalJSON
method for marshalling adjustments, and UnmarshalJSON
for changes to unmarshalling.
I find this approach really handy when I want the JSON structure to interact smoothly with my Go structs.
Let’s say, for instance, I aim to show a person’s academic degree before their name when the data is sent back to the client. I can accomplish this by employing custom marshalling as follows:
type Person struct {
Name string `json:"name"`
Degree string `json:"-"`
}
func (p Person) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
DisplayName string `json:"displayName"`
}{
DisplayName: fmt.Sprintf("%s %s", p.Degree, p.Name),
})
}
In this code snippet, I focus on marshalling the Person
struct. I build an anonymous struct on the fly that holds just a DisplayName
field. This DisplayName
combines the person's degree and name.
Here is what you get when you run the code:
aiden := Person{Name: "Aiden", Degree: "Ph.D"}
// marshal result: {"displayName":"Ph.D Aiden"}
And that’s it… all these tools and techniques can be seamlessly integrated into your projects, big or small.
So go ahead, start implementing what you’ve learned, take your Go coding skills to the next level.